910e62b5创建于 1月15日历史提交
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


#include "services/network/web_bundle/web_bundle_chunked_buffer.h"

#include <algorithm>

#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/not_fatal_until.h"
#include "base/numerics/checked_math.h"

namespace network {

namespace {

class ChunkedBufferDataSource : public mojo::DataPipeProducer::DataSource {
 public:
  ChunkedBufferDataSource(std::unique_ptr<const WebBundleChunkedBuffer> buffer,
                          uint64_t offset,
                          uint64_t length)
      : buffer_(std::move(buffer)), offset_(offset), length_(length) {
    DCHECK(buffer_);
  }
  ~ChunkedBufferDataSource() override = default;

  // Disallow copy and assign.
  ChunkedBufferDataSource(const ChunkedBufferDataSource&) = delete;
  ChunkedBufferDataSource& operator=(const ChunkedBufferDataSource&) = delete;

  uint64_t GetLength() const override { return length_; }

  ReadResult Read(uint64_t offset, base::span<char> buffer) override {
    ReadResult result;
    if (offset >= length_) {
      result.result = MOJO_RESULT_OUT_OF_RANGE;
      return result;
    }
    uint64_t read_start = offset_ + offset;
    uint64_t len = std::min<uint64_t>(length_ - offset, buffer.size());
    result.bytes_read = buffer_->ReadData(
        read_start,
        base::as_writable_bytes(buffer).first(base::checked_cast<size_t>(len)));
    return result;
  }

 private:
  const std::unique_ptr<const WebBundleChunkedBuffer> buffer_;
  const uint64_t offset_;
  const uint64_t length_;
};

}  // namespace

WebBundleChunkedBuffer::Chunk::Chunk(
    uint64_t start_pos,
    scoped_refptr<const base::RefCountedBytes> bytes)
    : start_pos_(start_pos), bytes_(std::move(bytes)) {
  DCHECK(bytes_);
  DCHECK(bytes_->size() != 0);
  CHECK(base::CheckAdd<uint64_t>(start_pos_, bytes_->size()).IsValid());
}

WebBundleChunkedBuffer::Chunk::~Chunk() = default;

WebBundleChunkedBuffer::Chunk::Chunk(const WebBundleChunkedBuffer::Chunk&) =
    default;
WebBundleChunkedBuffer::Chunk::Chunk(WebBundleChunkedBuffer::Chunk&&) = default;

WebBundleChunkedBuffer::WebBundleChunkedBuffer() = default;

WebBundleChunkedBuffer::WebBundleChunkedBuffer(ChunkVector chunks)
    : chunks_(std::move(chunks)) {}

WebBundleChunkedBuffer::~WebBundleChunkedBuffer() = default;

void WebBundleChunkedBuffer::Append(base::span<const uint8_t> data) {
  if (data.empty()) {
    return;
  }
  auto bytes = base::MakeRefCounted<base::RefCountedBytes>(data);
  chunks_.emplace_back(end_pos(), std::move(bytes));
}

bool WebBundleChunkedBuffer::ContainsAll(uint64_t offset,
                                         uint64_t length) const {
  DCHECK(base::CheckAdd<uint64_t>(offset, length).IsValid());
  if (length == 0)
    return true;
  if (offset < start_pos())
    return false;
  if (offset + length > end_pos())
    return false;
  return true;
}

std::unique_ptr<mojo::DataPipeProducer::DataSource>
WebBundleChunkedBuffer::CreateDataSource(uint64_t offset,
                                         uint64_t max_length) const {
  uint64_t length = GetAvailableLength(offset, max_length);
  if (length == 0)
    return nullptr;
  return std::make_unique<ChunkedBufferDataSource>(
      CreatePartialBuffer(offset, length), offset, length);
}

uint64_t WebBundleChunkedBuffer::size() const {
  DCHECK_GE(end_pos(), start_pos());
  return end_pos() - start_pos();
}

WebBundleChunkedBuffer::ChunkVector::const_iterator
WebBundleChunkedBuffer::FindChunk(uint64_t pos) const {
  if (empty())
    return chunks_.end();
  // |pos| ls before everything
  if (pos < chunks_.begin()->start_pos())
    return chunks_.end();
  // As an optimization, check the last region first
  if (chunks_.back().start_pos() <= pos) {
    if (chunks_.back().end_pos() <= pos)
      return chunks_.end();
    return chunks_.end() - 1;
  }
  // Binary search
  return std::partition_point(
      chunks_.begin(), chunks_.end(),
      [pos](const Chunk& chunk) { return chunk.end_pos() <= pos; });
}

std::unique_ptr<const WebBundleChunkedBuffer>
WebBundleChunkedBuffer::CreatePartialBuffer(uint64_t offset,
                                            uint64_t length) const {
  DCHECK(ContainsAll(offset, length));
  ChunkVector::const_iterator it = FindChunk(offset);
  CHECK(it != chunks_.end());
  ChunkVector new_chunks;
  while (it != chunks_.end() && it->start_pos() < offset + length) {
    new_chunks.push_back(*it);
    ++it;
  }
  return base::WrapUnique(new WebBundleChunkedBuffer(std::move(new_chunks)));
}

bool WebBundleChunkedBuffer::empty() const {
  return chunks_.empty();
}

uint64_t WebBundleChunkedBuffer::start_pos() const {
  if (empty())
    return 0;
  return chunks_.front().start_pos();
}

uint64_t WebBundleChunkedBuffer::end_pos() const {
  if (empty())
    return 0;
  return chunks_.back().end_pos();
}

uint64_t WebBundleChunkedBuffer::GetAvailableLength(uint64_t offset,
                                                    uint64_t max_length) const {
  if (offset < start_pos())
    return 0;
  if (end_pos() <= offset)
    return 0;
  return std::min(max_length, end_pos() - offset);
}

uint64_t WebBundleChunkedBuffer::ReadData(uint64_t offset,
                                          base::span<uint8_t> out) const {
  uint64_t length = GetAvailableLength(offset, out.size());
  if (length == 0) {
    return 0;
  }
  ChunkVector::const_iterator it = FindChunk(offset);
  uint64_t written = 0;
  while (length > written && it != chunks_.end()) {
    auto it_span = base::span(*it);
    uint64_t offset_in_chunk = offset + written - it->start_pos();
    uint64_t length_in_chunk =
        std::min(it->size() - offset_in_chunk, length - written);
    size_t checked_offset = base::checked_cast<size_t>(offset_in_chunk);
    size_t checked_length = base::checked_cast<size_t>(length_in_chunk);
    out.copy_prefix_from(it_span.subspan(checked_offset, checked_length));
    out = out.subspan(checked_length);
    written += checked_length;
    ++it;
  }
  return written;
}

}  // namespace network