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 |