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..a1d94c62b1a31801a62a3b9f984e06a5417c9115 |
--- /dev/null |
+++ b/storage/browser/blob/blob_transport_strategy.cc |
@@ -0,0 +1,239 @@ |
+// 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( |
+ SizeType total_memory_size, SizeType max_segment_size, |
+ std::vector<SizeType>* segment_sizes, |
+ std::vector<BlobTransportStrategy::MemoryItemRequest>* requests) |
kinuko
2015/04/30 15:04:21
nit: one parameter on each line (here and below)
dmurph
2015/05/07 02:07:39
Done.
|
+ : segment_sizes(segment_sizes), |
+ requests(requests), |
+ storage_element_index(0) { |
+ SizeType memory_left = total_memory_size; |
+ SizeType segment_size = 0; |
+ 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/04/30 15:04:21
What is this segment_sizes for? (Not used?) In the
kinuko
2015/04/30 15:04:21
nit: I'd keep segment_size within the loop (reads
dmurph
2015/05/07 02:07:38
Done.
|
+ } |
+ |
+ void operator()(DataElement::Type type, size_t element_index, |
kinuko
2015/04/30 15:04:21
nit: in chromium code I've hardly seen operator()
dmurph
2015/05/07 02:07:38
I could turn these into lambdas as well. All the
|
+ 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; |
kinuko
2015/04/30 15:04:21
is storage_element_index == element_index always t
dmurph
2015/05/07 02:07:39
No, as the element index can stay the same for mul
|
+ 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; |
kinuko
2015/04/30 15:04:21
nit: why the order of indices/sizes different in f
dmurph
2015/05/07 02:07:38
No reason, which way do you prefer?
|
+ request.message.handle_index = segment_index; |
+ request.message.handle_offset = segment_offset; |
kinuko
2015/04/30 15:04:21
nit: Intentionally not using BlobItemBytesRequest:
dmurph
2015/05/07 02:07:38
Yes, it's a little more readable, as you can read
|
+ |
+ requests->push_back(request); |
+ storage_element_index++; |
+ }; |
+ |
+ std::vector<SizeType>* segment_sizes; |
+ 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 total_memory_size, SizeType max_segment_size, |
+ std::vector<SizeType>* segment_sizes, |
+ std::vector<BlobTransportStrategy::MemoryItemRequest>* requests) |
+ : segment_sizes(segment_sizes), |
+ requests(requests), |
+ max_segment_size(max_segment_size), |
+ storage_element_index(0), |
+ storage_element_offset(0) { |
+ SizeType memory_left = total_memory_size; |
+ SizeType segment_size = 0; |
+ for (; memory_left > 0; memory_left -= segment_size) { |
+ segment_size = std::min(max_segment_size, memory_left); |
+ segment_sizes->push_back(segment_size); |
+ } |
+ } |
+ |
+ 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 += 2; |
kinuko
2015/04/30 15:04:21
I think I'm lost here; why do we add 2?
dmurph
2015/05/07 02:07:38
Added comment.
|
+ 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<SizeType>* segment_sizes; |
+ std::vector<BlobTransportStrategy::MemoryItemRequest>* requests; |
+ |
+ SizeType max_segment_size; |
+ size_t storage_element_index; |
+ SizeType storage_element_offset; |
+}; |
+} // namespace |
+ |
+template <typename ForEachFunctor, typename SizeType> |
+/* static */ void BlobTransportStrategy::ForEachWithSegment( |
kinuko
2015/04/30 15:04:21
nit: same method order in .h and in .cc
dmurph
2015/05/07 02:07:38
Done.
|
+ 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; |
+ } |
+ } |
+} |
+ |
+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; |
kinuko
2015/04/30 15:04:21
Could Initialize() be called multiple times? Is th
dmurph
2015/05/07 02:07:38
Yes it can, it's reusable.
|
+ |
+ uint64_t total_memory_size = 0; |
+ size_t memory_items = 0; |
kinuko
2015/04/30 15:04:21
Not used after line 177?
If it's for the # of mem
dmurph
2015/05/07 02:07:39
Removed.
|
+ for (const auto& info : blob_item_infos) { |
+ if (info.type() != DataElement::TYPE_BYTES) { |
+ continue; |
+ } |
+ ++memory_items; |
+ 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) { |
kinuko
2015/04/30 15:04:21
max_blob_in_memory_size == kMaxBlobSize (e.g. 400M
dmurph
2015/05/07 02:07:39
Yes, here we know that we need to save to disk.
|
+ if (total_memory_size > disk_space_left) { |
+ error_ = BlobTransportStrategy::ERROR_TOO_LARGE; |
+ return; |
+ } |
+ FileStorageStrategy strategy(total_memory_size, max_file_size, |
+ &file_handles_, &requests_); |
+ BlobTransportStrategy::ForEachWithSegment(blob_item_infos, |
+ 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) { |
+ DCHECK_LE(total_memory_size, std::numeric_limits<size_t>::max()); |
kinuko
2015/04/30 15:04:21
If blob_item_infos comes over IPC this cannot be g
dmurph
2015/05/07 02:07:38
Good catch, changed to CHECK. This should still n
|
+ SharedMemoryStorageStrategy strategy(total_memory_size, |
+ max_shared_memory_size, |
+ &shared_memory_handles_, &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); |
+ } |
+} |
+ |
+} // namespace storage |