Chromium Code Reviews| Index: storage/browser/blob/blob_transport_strategy.cc |
| diff --git a/storage/browser/blob/blob_transport_strategy.cc b/storage/browser/blob/blob_transport_strategy.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1ebb13f393ed73e362dc7adfd487a62bcfe40012 |
| --- /dev/null |
| +++ b/storage/browser/blob/blob_transport_strategy.cc |
| @@ -0,0 +1,242 @@ |
| +// Copyright 2015 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 "storage/browser/blob/blob_transport_strategy.h" |
| + |
| +#include <algorithm> |
| + |
| +namespace storage { |
| + |
| +namespace { |
| + |
| +// This class handles the logic of how transported memory is going to be |
| +// represented as storage in the browser. The main idea is that all the memory |
| +// is now packed into file chunks, and the browser items will just reference |
| +// the file with offsets and sizes. |
| +struct FileStorageStrategy { |
| + typedef uint64_t SizeType; |
| + |
| + FileStorageStrategy( |
| + std::vector<BlobTransportStrategy::MemoryItemRequest>* requests) |
| + : requests(requests), storage_element_index(0) {} |
| + |
| + void operator()(DataElement::Type type, |
| + size_t element_index, |
| + SizeType element_offset, |
| + size_t segment_index, |
| + SizeType segment_offset, |
| + SizeType size) { |
| + if (type != DataElement::TYPE_BYTES) { |
| + storage_element_index++; |
| + return; |
| + } |
| + BlobTransportStrategy::MemoryItemRequest request; |
| + request.browser_item_index = storage_element_index; |
| + request.browser_item_offset = 0; |
| + request.message.request_number = requests->size(); |
| + request.message.transport_strategy = IPC_BLOB_ITEM_REQUEST_STRATEGY_FILE; |
| + request.message.renderer_item_index = element_index; |
| + request.message.renderer_item_offset = element_offset; |
| + request.message.size = size; |
| + request.message.handle_index = segment_index; |
| + request.message.handle_offset = segment_offset; |
| + |
| + requests->push_back(request); |
| + storage_element_index++; |
| + }; |
| + |
| + std::vector<BlobTransportStrategy::MemoryItemRequest>* requests; |
| + |
| + size_t storage_element_index; |
| +}; |
| + |
| +// This class handles the logic of storing memory that is transported as |
| +// consolidated shared memory. The main hurdle is to re-separate memory blocks |
| +// that cross file or blob boundaries. |
| +struct SharedMemoryStorageStrategy { |
| + typedef size_t SizeType; |
| + |
| + SharedMemoryStorageStrategy( |
| + SizeType max_segment_size, |
| + std::vector<BlobTransportStrategy::MemoryItemRequest>* requests) |
| + : requests(requests), |
| + max_segment_size(max_segment_size), |
| + storage_element_index(0), |
| + storage_element_offset(0) {} |
| + |
| + void operator()(DataElement::Type type, |
| + size_t element_index, |
| + SizeType element_offset, |
| + size_t segment_index, |
| + SizeType segment_offset, |
| + SizeType size) { |
| + if (type != DataElement::TYPE_BYTES) { |
| + // We add 1 to skip the non-bytes storage element, and we add 1 again |
| + // because we need to start a new request (requests don't span blob |
| + // or file boundaries). Thus, +2. |
| + storage_element_index += 2; |
| + storage_element_offset = 0; |
| + return; |
| + } |
| + if (storage_element_offset + size > max_segment_size) { |
| + storage_element_index++; |
| + storage_element_offset = 0; |
| + } |
| + BlobTransportStrategy::MemoryItemRequest request; |
| + request.browser_item_index = storage_element_index; |
| + request.browser_item_offset = storage_element_offset; |
| + request.message.request_number = requests->size(); |
| + request.message.transport_strategy = |
| + IPC_BLOB_ITEM_REQUEST_STRATEGY_SHARED_MEMORY; |
| + request.message.renderer_item_index = element_index; |
| + request.message.renderer_item_offset = element_offset; |
| + request.message.size = size; |
| + request.message.handle_index = segment_index; |
| + request.message.handle_offset = segment_offset; |
| + |
| + requests->push_back(request); |
| + storage_element_offset += size; |
| + }; |
| + |
| + std::vector<BlobTransportStrategy::MemoryItemRequest>* requests; |
| + |
| + SizeType max_segment_size; |
| + size_t storage_element_index; |
| + SizeType storage_element_offset; |
| +}; |
| +} // namespace |
| + |
| +BlobTransportStrategy::BlobTransportStrategy() |
| + : error_(BlobTransportStrategy::ERROR_NONE) {} |
| + |
| +BlobTransportStrategy::~BlobTransportStrategy() {} |
| + |
| +void BlobTransportStrategy::Initialize( |
| + BlobTransportStrategy::Mode mode, |
| + size_t max_ipc_memory_size, |
| + size_t max_shared_memory_size, |
| + uint64_t max_file_size, |
| + size_t max_blob_in_memory_size, |
| + uint64_t disk_space_left, |
| + size_t memory_left, |
| + const std::vector<DataElement>& blob_item_infos) { |
| + file_handles_.clear(); |
| + shared_memory_handles_.clear(); |
| + requests_.clear(); |
| + error_ = BlobTransportStrategy::ERROR_NONE; |
| + |
| + uint64_t total_memory_size = 0; |
| + for (const auto& info : blob_item_infos) { |
| + if (info.type() != DataElement::TYPE_BYTES) { |
| + continue; |
| + } |
|
kinuko
2015/05/25 08:32:02
nit: no { } required for single-line body in chrom
dmurph
2015/06/13 00:13:10
Done.
|
| + total_memory_size += info.length(); |
| + } |
| + |
| + switch (mode) { |
| + case BlobTransportStrategy::MODE_DISK: |
| + if (total_memory_size > |
| + disk_space_left + static_cast<uint64_t>(memory_left)) { |
| + error_ = BlobTransportStrategy::ERROR_TOO_LARGE; |
| + return; |
| + } |
| + if (total_memory_size > max_blob_in_memory_size) { |
| + if (total_memory_size > disk_space_left) { |
| + error_ = BlobTransportStrategy::ERROR_TOO_LARGE; |
| + return; |
| + } |
| + CreateHandleSizes(total_memory_size, max_file_size, &file_handles_); |
| + FileStorageStrategy strategy(&requests_); |
| + BlobTransportStrategy::ForEachWithSegment(blob_item_infos, |
|
kinuko
2015/05/25 08:32:02
nit: we probably don't need BlobTransportStorategy
dmurph
2015/06/13 00:13:10
Done.
|
| + max_file_size, strategy); |
| + return; |
| + } |
| + break; |
| + case BlobTransportStrategy::MODE_NO_DISK: |
| + if (total_memory_size > memory_left) { |
| + error_ = BlobTransportStrategy::ERROR_TOO_LARGE; |
| + return; |
| + } |
| + break; |
| + default: |
| + NOTREACHED() << "Invalid mode " << mode; |
| + error_ = BlobTransportStrategy::ERROR_INVALID_PARAMS; |
| + return; |
| + } |
| + if (total_memory_size > max_ipc_memory_size) { |
| + CHECK_LE(total_memory_size, std::numeric_limits<size_t>::max()); |
| + CreateHandleSizes(static_cast<size_t>(total_memory_size), |
| + max_shared_memory_size, &shared_memory_handles_); |
| + SharedMemoryStorageStrategy strategy(max_shared_memory_size, &requests_); |
| + BlobTransportStrategy::ForEachWithSegment(blob_item_infos, |
| + max_shared_memory_size, strategy); |
| + return; |
| + } |
| + size_t items_length = blob_item_infos.size(); |
| + for (size_t i = 0; i < items_length; i++) { |
| + const auto& info = blob_item_infos.at(i); |
| + if (info.type() != DataElement::TYPE_BYTES) { |
| + continue; |
| + } |
| + BlobTransportStrategy::MemoryItemRequest request; |
| + request.browser_item_index = i; |
| + request.browser_item_offset = 0; |
| + request.message.request_number = requests_.size(); |
| + request.message.transport_strategy = |
| + storage::IPC_BLOB_ITEM_REQUEST_STRATEGY_IPC; |
| + request.message.renderer_item_index = i; |
| + request.message.renderer_item_offset = 0; |
| + request.message.size = info.length(); |
| + requests_.push_back(request); |
|
kinuko
2015/05/25 08:32:03
So in the current design the decision is basically
dmurph
2015/06/13 00:13:10
Yes.
|
| + } |
| +} |
| + |
| +template <typename ForEachFunctor, typename SizeType> |
| +/* static */ void BlobTransportStrategy::ForEachWithSegment( |
| + const std::vector<DataElement>& items, |
| + SizeType max_segment_size, |
| + ForEachFunctor function) { |
| + DCHECK_GT(max_segment_size, 0ull); |
| + size_t segment_index = 0; |
| + SizeType segment_offset = 0; |
| + size_t items_length = items.size(); |
| + for (size_t element_index = 0; element_index < items_length; |
| + ++element_index) { |
| + const auto& element = items.at(element_index); |
| + if (element.type() != DataElement::TYPE_BYTES) { |
| + function(element.type(), element_index, 0, 0, 0, 0); |
| + continue; |
| + } |
| + SizeType element_memory_left = element.length(); |
| + SizeType element_offset = 0; |
| + while (element_memory_left > 0) { |
| + if (segment_offset == max_segment_size) { |
| + ++segment_index; |
| + segment_offset = 0; |
| + } |
| + SizeType memory_writing = |
| + std::min(max_segment_size - segment_offset, element_memory_left); |
| + function(DataElement::TYPE_BYTES, element_index, element_offset, |
| + segment_index, segment_offset, memory_writing); |
| + element_memory_left -= memory_writing; |
| + segment_offset += memory_writing; |
| + element_offset += memory_writing; |
| + } |
| + } |
| +} |
| + |
| +template <typename SizeType> |
| +/* static */ void BlobTransportStrategy::CreateHandleSizes( |
| + SizeType total_memory_size, |
| + SizeType max_segment_size, |
| + std::vector<SizeType>* segment_sizes) { |
| + SizeType memory_left = total_memory_size; |
| + SizeType segment_size; |
| + for (; memory_left > 0; memory_left -= segment_size) { |
| + segment_size = std::min(max_segment_size, memory_left); |
| + segment_sizes->push_back(segment_size); |
| + } |
|
kinuko
2015/05/25 08:32:03
I have a feeling that this is a bit verbose and co
dmurph
2015/06/13 00:13:10
I'm scared that it will make that function too com
|
| +} |
| + |
| +} // namespace storage |