blob: 8d5a84b5db522434f65ab3df2107c12c8d63cced [file] [log] [blame]
// Copyright 2021 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.
#include "services/network/web_bundle_chunked_buffer.h"
#include <algorithm>
#include "base/check.h"
#include "base/memory/ptr_util.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 out_size = buffer.size();
uint64_t len = std::min(length_ - offset, out_size);
buffer_->ReadData(read_start, len,
reinterpret_cast<uint8_t*>(buffer.data()));
result.bytes_read = 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);
DCHECK(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;
uint64_t WebBundleChunkedBuffer::Chunk::start_pos() const {
return start_pos_;
}
uint64_t WebBundleChunkedBuffer::Chunk::end_pos() const {
return start_pos_ + bytes_->size();
}
uint64_t WebBundleChunkedBuffer::Chunk::size() const {
return bytes_->size();
}
const uint8_t* WebBundleChunkedBuffer::Chunk::data() const {
return bytes_->data().data();
}
WebBundleChunkedBuffer::WebBundleChunkedBuffer() = default;
WebBundleChunkedBuffer::WebBundleChunkedBuffer(ChunkVector chunks)
: chunks_(std::move(chunks)) {}
WebBundleChunkedBuffer::~WebBundleChunkedBuffer() = default;
void WebBundleChunkedBuffer::Append(const uint8_t* data, size_t num_bytes) {
DCHECK(base::CheckAdd<uint64_t>(end_pos(), num_bytes).IsValid());
if (num_bytes == 0)
return;
auto bytes = base::MakeRefCounted<base::RefCountedBytes>(data, num_bytes);
chunks_.push_back(Chunk(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,
uint64_t max_length,
uint8_t* out) const {
uint64_t length = GetAvailableLength(offset, max_length);
if (length == 0)
return 0;
ChunkVector::const_iterator it = FindChunk(offset);
DCHECK(it != chunks_.end());
uint64_t written = 0;
while (length > written) {
DCHECK(it != chunks_.end());
uint64_t offset_in_chunk = offset + written - it->start_pos();
uint64_t length_in_chunk =
std::min(it->size() - offset_in_chunk, length - written);
memcpy(out + written, it->data() + offset_in_chunk, length_in_chunk);
written += length_in_chunk;
++it;
}
return written;
}
} // namespace network