| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stddef.h> | 5 #include <stddef.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 | 9 |
| 10 #include "base/numerics/safe_math.h" | 10 #include "base/numerics/safe_math.h" |
| 11 #include "storage/browser/blob/blob_async_transport_strategy.h" | 11 #include "storage/browser/blob/blob_async_transport_request_builder.h" |
| 12 #include "storage/common/blob_storage/blob_storage_constants.h" | 12 #include "storage/common/blob_storage/blob_storage_constants.h" |
| 13 | 13 |
| 14 namespace storage { | 14 namespace storage { |
| 15 namespace { | 15 namespace { |
| 16 bool IsBytes(DataElement::Type type) { | 16 bool IsBytes(DataElement::Type type) { |
| 17 return type == DataElement::TYPE_BYTES || | 17 return type == DataElement::TYPE_BYTES || |
| 18 type == DataElement::TYPE_BYTES_DESCRIPTION; | 18 type == DataElement::TYPE_BYTES_DESCRIPTION; |
| 19 } | 19 } |
| 20 | 20 |
| 21 // This is the general template that each strategy below implements. See the | 21 // This is the general template that each strategy below implements. See the |
| 22 // ForEachWithSegment method for a description of how these are called. | 22 // ForEachWithSegment method for a description of how these are called. |
| 23 // class BlobSegmentVisitor { | 23 // class BlobSegmentVisitor { |
| 24 // public: | 24 // public: |
| 25 // typedef ___ SizeType; | 25 // typedef ___ SizeType; |
| 26 // void VisitBytesSegment(size_t element_index, uint64_t element_offset, | 26 // void VisitBytesSegment(size_t element_index, uint64_t element_offset, |
| 27 // size_t segment_index, uint64_t segment_offset, | 27 // size_t segment_index, uint64_t segment_offset, |
| 28 // uint64_t size); | 28 // uint64_t size); |
| 29 // void VisitNonBytesSegment(const DataElement& element, size_t element_idx); | 29 // void VisitNonBytesSegment(const DataElement& element, size_t element_idx); |
| 30 // void Done(); | 30 // void Done(); |
| 31 // }; | 31 // }; |
| 32 | 32 |
| 33 // This class handles the logic of how transported memory is going to be | 33 // This class handles the logic of how transported memory is going to be |
| 34 // represented as storage in the browser. The main idea is that all the memory | 34 // represented as storage in the browser. The main idea is that all the memory |
| 35 // is now packed into file chunks, and the browser items will just reference | 35 // is now packed into file chunks, and the browser items will just reference |
| 36 // the file with offsets and sizes. | 36 // the file with offsets and sizes. |
| 37 class FileStorageStrategy { | 37 class FileStorageStrategy { |
| 38 public: | 38 public: |
| 39 FileStorageStrategy( | 39 FileStorageStrategy( |
| 40 std::vector<BlobAsyncTransportStrategy::RendererMemoryItemRequest>* | 40 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* |
| 41 requests, | 41 requests, |
| 42 BlobDataBuilder* builder) | 42 BlobDataBuilder* builder) |
| 43 : requests(requests), builder(builder), current_item_index(0) {} | 43 : requests(requests), builder(builder), current_item_index(0) {} |
| 44 | 44 |
| 45 ~FileStorageStrategy() {} | 45 ~FileStorageStrategy() {} |
| 46 | 46 |
| 47 void VisitBytesSegment(size_t element_index, | 47 void VisitBytesSegment(size_t element_index, |
| 48 uint64_t element_offset, | 48 uint64_t element_offset, |
| 49 size_t segment_index, | 49 size_t segment_index, |
| 50 uint64_t segment_offset, | 50 uint64_t segment_offset, |
| 51 uint64_t size) { | 51 uint64_t size) { |
| 52 BlobAsyncTransportStrategy::RendererMemoryItemRequest request; | 52 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; |
| 53 request.browser_item_index = current_item_index; | 53 request.browser_item_index = current_item_index; |
| 54 request.browser_item_offset = 0; | 54 request.browser_item_offset = 0; |
| 55 request.message.request_number = requests->size(); | 55 request.message.request_number = requests->size(); |
| 56 request.message.transport_strategy = IPCBlobItemRequestStrategy::FILE; | 56 request.message.transport_strategy = IPCBlobItemRequestStrategy::FILE; |
| 57 request.message.renderer_item_index = element_index; | 57 request.message.renderer_item_index = element_index; |
| 58 request.message.renderer_item_offset = element_offset; | 58 request.message.renderer_item_offset = element_offset; |
| 59 request.message.size = size; | 59 request.message.size = size; |
| 60 request.message.handle_index = segment_index; | 60 request.message.handle_index = segment_index; |
| 61 request.message.handle_offset = segment_offset; | 61 request.message.handle_offset = segment_offset; |
| 62 | 62 |
| 63 requests->push_back(request); | 63 requests->push_back(request); |
| 64 builder->AppendFutureFile(segment_offset, size); | 64 builder->AppendFutureFile(segment_offset, size); |
| 65 current_item_index++; | 65 current_item_index++; |
| 66 } | 66 } |
| 67 | 67 |
| 68 void VisitNonBytesSegment(const DataElement& element, size_t element_index) { | 68 void VisitNonBytesSegment(const DataElement& element, size_t element_index) { |
| 69 builder->AppendIPCDataElement(element); | 69 builder->AppendIPCDataElement(element); |
| 70 current_item_index++; | 70 current_item_index++; |
| 71 } | 71 } |
| 72 | 72 |
| 73 void Done() {} | 73 void Done() {} |
| 74 | 74 |
| 75 std::vector<BlobAsyncTransportStrategy::RendererMemoryItemRequest>* requests; | 75 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* |
| 76 requests; |
| 76 BlobDataBuilder* builder; | 77 BlobDataBuilder* builder; |
| 77 | 78 |
| 78 size_t current_item_index; | 79 size_t current_item_index; |
| 79 }; | 80 }; |
| 80 | 81 |
| 81 // This class handles the logic of storing memory that is transported as | 82 // This class handles the logic of storing memory that is transported as |
| 82 // consolidated shared memory. | 83 // consolidated shared memory. |
| 83 class SharedMemoryStorageStrategy { | 84 class SharedMemoryStorageStrategy { |
| 84 public: | 85 public: |
| 85 SharedMemoryStorageStrategy( | 86 SharedMemoryStorageStrategy( |
| 86 size_t max_segment_size, | 87 size_t max_segment_size, |
| 87 std::vector<BlobAsyncTransportStrategy::RendererMemoryItemRequest>* | 88 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* |
| 88 requests, | 89 requests, |
| 89 BlobDataBuilder* builder) | 90 BlobDataBuilder* builder) |
| 90 : requests(requests), | 91 : requests(requests), |
| 91 max_segment_size(max_segment_size), | 92 max_segment_size(max_segment_size), |
| 92 builder(builder), | 93 builder(builder), |
| 93 current_item_size(0), | 94 current_item_size(0), |
| 94 current_item_index(0) {} | 95 current_item_index(0) {} |
| 95 ~SharedMemoryStorageStrategy() {} | 96 ~SharedMemoryStorageStrategy() {} |
| 96 | 97 |
| 97 void VisitBytesSegment(size_t element_index, | 98 void VisitBytesSegment(size_t element_index, |
| 98 uint64_t element_offset, | 99 uint64_t element_offset, |
| 99 size_t segment_index, | 100 size_t segment_index, |
| 100 uint64_t segment_offset, | 101 uint64_t segment_offset, |
| 101 uint64_t size) { | 102 uint64_t size) { |
| 102 if (current_item_size + size > max_segment_size) { | 103 if (current_item_size + size > max_segment_size) { |
| 103 builder->AppendFutureData(current_item_size); | 104 builder->AppendFutureData(current_item_size); |
| 104 current_item_index++; | 105 current_item_index++; |
| 105 current_item_size = 0; | 106 current_item_size = 0; |
| 106 } | 107 } |
| 107 BlobAsyncTransportStrategy::RendererMemoryItemRequest request; | 108 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; |
| 108 request.browser_item_index = current_item_index; | 109 request.browser_item_index = current_item_index; |
| 109 request.browser_item_offset = current_item_size; | 110 request.browser_item_offset = current_item_size; |
| 110 request.message.request_number = requests->size(); | 111 request.message.request_number = requests->size(); |
| 111 request.message.transport_strategy = | 112 request.message.transport_strategy = |
| 112 IPCBlobItemRequestStrategy::SHARED_MEMORY; | 113 IPCBlobItemRequestStrategy::SHARED_MEMORY; |
| 113 request.message.renderer_item_index = element_index; | 114 request.message.renderer_item_index = element_index; |
| 114 request.message.renderer_item_offset = element_offset; | 115 request.message.renderer_item_offset = element_offset; |
| 115 request.message.size = size; | 116 request.message.size = size; |
| 116 request.message.handle_index = segment_index; | 117 request.message.handle_index = segment_index; |
| 117 request.message.handle_offset = segment_offset; | 118 request.message.handle_offset = segment_offset; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 129 current_item_index++; | 130 current_item_index++; |
| 130 current_item_size = 0; | 131 current_item_size = 0; |
| 131 } | 132 } |
| 132 | 133 |
| 133 void Done() { | 134 void Done() { |
| 134 if (current_item_size != 0) { | 135 if (current_item_size != 0) { |
| 135 builder->AppendFutureData(current_item_size); | 136 builder->AppendFutureData(current_item_size); |
| 136 } | 137 } |
| 137 } | 138 } |
| 138 | 139 |
| 139 std::vector<BlobAsyncTransportStrategy::RendererMemoryItemRequest>* requests; | 140 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* |
| 141 requests; |
| 140 | 142 |
| 141 size_t max_segment_size; | 143 size_t max_segment_size; |
| 142 BlobDataBuilder* builder; | 144 BlobDataBuilder* builder; |
| 143 size_t current_item_size; | 145 size_t current_item_size; |
| 144 uint64_t current_item_index; | 146 uint64_t current_item_index; |
| 145 }; | 147 }; |
| 146 | 148 |
| 147 // This iterates of the data elements and segments the 'bytes' data into | 149 // This iterates of the data elements and segments the 'bytes' data into |
| 148 // the smallest number of segments given the max_segment_size. | 150 // the smallest number of segments given the max_segment_size. |
| 149 // The callback describes either: | 151 // The callback describes either: |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 segment_offset, memory_writing); | 186 segment_offset, memory_writing); |
| 185 element_memory_left -= memory_writing; | 187 element_memory_left -= memory_writing; |
| 186 segment_offset += memory_writing; | 188 segment_offset += memory_writing; |
| 187 element_offset += memory_writing; | 189 element_offset += memory_writing; |
| 188 } | 190 } |
| 189 } | 191 } |
| 190 visitor->Done(); | 192 visitor->Done(); |
| 191 } | 193 } |
| 192 } // namespace | 194 } // namespace |
| 193 | 195 |
| 194 BlobAsyncTransportStrategy::RendererMemoryItemRequest:: | 196 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest:: |
| 195 RendererMemoryItemRequest() | 197 RendererMemoryItemRequest() |
| 196 : browser_item_index(0), browser_item_offset(0), received(false) {} | 198 : browser_item_index(0), browser_item_offset(0) {} |
| 197 | 199 |
| 198 BlobAsyncTransportStrategy::BlobAsyncTransportStrategy() | 200 BlobAsyncTransportRequestBuilder::BlobAsyncTransportRequestBuilder() |
| 199 : error_(BlobAsyncTransportStrategy::ERROR_NONE), total_bytes_size_(0) {} | 201 : total_bytes_size_(0) {} |
| 200 | 202 |
| 201 BlobAsyncTransportStrategy::~BlobAsyncTransportStrategy() {} | 203 BlobAsyncTransportRequestBuilder::~BlobAsyncTransportRequestBuilder() {} |
| 202 | 204 |
| 203 // if total_blob_size > |memory_available| (say 400MB) | 205 // Initializes the transport strategy for file requests. |
| 204 // Request all data in files | 206 void BlobAsyncTransportRequestBuilder::InitializeForFileRequests( |
| 205 // (Segment all of the existing data into | 207 size_t max_file_size, |
| 206 // file blocks, of <= |max_file_size|) | 208 uint64_t blob_total_size, |
| 207 // else if total_blob_size > |max_ipc_memory_size| (say 150KB) | 209 const std::vector<DataElement>& elements, |
| 208 // Request all data in shared memory | 210 BlobDataBuilder* builder) { |
| 209 // (Segment all of the existing data into | 211 DCHECK(requests_.empty()); |
| 210 // shared memory blocks, of <= |max_shared_memory_size|) | 212 total_bytes_size_ = blob_total_size; |
| 211 // else | 213 ComputeHandleSizes(total_bytes_size_, max_file_size, &file_sizes_); |
| 212 // Request all data to be sent over IPC | 214 FileStorageStrategy strategy(&requests_, builder); |
| 213 void BlobAsyncTransportStrategy::Initialize( | 215 ForEachWithSegment(elements, static_cast<uint64_t>(max_file_size), &strategy); |
| 216 } |
| 217 |
| 218 void BlobAsyncTransportRequestBuilder::InitializeForSharedMemoryRequests( |
| 219 size_t max_shared_memory_size, |
| 220 uint64_t blob_total_size, |
| 221 const std::vector<DataElement>& elements, |
| 222 BlobDataBuilder* builder) { |
| 223 DCHECK(requests_.empty()); |
| 224 DCHECK(blob_total_size <= std::numeric_limits<size_t>::max()); |
| 225 total_bytes_size_ = blob_total_size; |
| 226 ComputeHandleSizes(total_bytes_size_, max_shared_memory_size, |
| 227 &shared_memory_sizes_); |
| 228 SharedMemoryStorageStrategy strategy(max_shared_memory_size, &requests_, |
| 229 builder); |
| 230 ForEachWithSegment(elements, static_cast<uint64_t>(max_shared_memory_size), |
| 231 &strategy); |
| 232 } |
| 233 |
| 234 void BlobAsyncTransportRequestBuilder::InitializeForIPCRequests( |
| 214 size_t max_ipc_memory_size, | 235 size_t max_ipc_memory_size, |
| 215 size_t max_shared_memory_size, | 236 uint64_t blob_total_size, |
| 216 size_t max_file_size, | 237 const std::vector<DataElement>& elements, |
| 217 uint64_t disk_space_left, | 238 BlobDataBuilder* builder) { |
| 218 size_t memory_available, | |
| 219 const std::string& uuid, | |
| 220 const std::vector<DataElement>& blob_item_infos) { | |
| 221 DCHECK(handle_sizes_.empty()); | |
| 222 DCHECK(requests_.empty()); | 239 DCHECK(requests_.empty()); |
| 223 DCHECK(!builder_.get()); | 240 // We don't segment anything, and just request the memory items directly |
| 224 builder_.reset(new BlobDataBuilder(uuid)); | 241 // in IPC. |
| 225 error_ = BlobAsyncTransportStrategy::ERROR_NONE; | 242 size_t items_length = elements.size(); |
| 226 | 243 total_bytes_size_ = blob_total_size; |
| 227 size_t memory_items = 0; | 244 for (size_t i = 0; i < items_length; i++) { |
| 228 base::CheckedNumeric<uint64_t> total_size_checked = 0; | 245 const auto& info = elements.at(i); |
| 229 for (const auto& info : blob_item_infos) { | |
| 230 if (!IsBytes(info.type())) { | 246 if (!IsBytes(info.type())) { |
| 247 builder->AppendIPCDataElement(info); |
| 231 continue; | 248 continue; |
| 232 } | 249 } |
| 233 total_size_checked += info.length(); | 250 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; |
| 234 ++memory_items; | |
| 235 } | |
| 236 | |
| 237 if (!total_size_checked.IsValid()) { | |
| 238 DVLOG(1) << "Impossible total size of all memory elements."; | |
| 239 error_ = BlobAsyncTransportStrategy::ERROR_INVALID_PARAMS; | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 total_bytes_size_ = total_size_checked.ValueOrDie(); | |
| 244 | |
| 245 // See if we have enough memory. | |
| 246 if (total_bytes_size_ > | |
| 247 disk_space_left + static_cast<uint64_t>(memory_available)) { | |
| 248 error_ = BlobAsyncTransportStrategy::ERROR_TOO_LARGE; | |
| 249 return; | |
| 250 } | |
| 251 | |
| 252 // If we're more than the available memory, then we're going straight to disk. | |
| 253 if (total_bytes_size_ > memory_available) { | |
| 254 if (total_bytes_size_ > disk_space_left) { | |
| 255 error_ = BlobAsyncTransportStrategy::ERROR_TOO_LARGE; | |
| 256 return; | |
| 257 } | |
| 258 ComputeHandleSizes(total_bytes_size_, max_file_size, &handle_sizes_); | |
| 259 FileStorageStrategy strategy(&requests_, builder_.get()); | |
| 260 ForEachWithSegment(blob_item_infos, static_cast<uint64_t>(max_file_size), | |
| 261 &strategy); | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 if (total_bytes_size_ > max_ipc_memory_size) { | |
| 266 // Note: The size must be <= std::numeric_limits<size_t>::max(). Otherwise | |
| 267 // we are guarenteed to be caught by the if statement above, | |
| 268 // |total_bytes_size_ > memory_available|. | |
| 269 ComputeHandleSizes(total_bytes_size_, max_shared_memory_size, | |
| 270 &handle_sizes_); | |
| 271 SharedMemoryStorageStrategy strategy(max_shared_memory_size, &requests_, | |
| 272 builder_.get()); | |
| 273 ForEachWithSegment(blob_item_infos, | |
| 274 static_cast<uint64_t>(max_shared_memory_size), | |
| 275 &strategy); | |
| 276 return; | |
| 277 } | |
| 278 | |
| 279 // Since they can all fit in IPC memory, we don't need to segment anything, | |
| 280 // and just request them straight in IPC. | |
| 281 size_t items_length = blob_item_infos.size(); | |
| 282 for (size_t i = 0; i < items_length; i++) { | |
| 283 const auto& info = blob_item_infos.at(i); | |
| 284 if (!IsBytes(info.type())) { | |
| 285 builder_->AppendIPCDataElement(info); | |
| 286 continue; | |
| 287 } | |
| 288 BlobAsyncTransportStrategy::RendererMemoryItemRequest request; | |
| 289 request.browser_item_index = i; | 251 request.browser_item_index = i; |
| 290 request.browser_item_offset = 0; | 252 request.browser_item_offset = 0; |
| 291 request.message.request_number = requests_.size(); | 253 request.message.request_number = requests_.size(); |
| 292 request.message.transport_strategy = IPCBlobItemRequestStrategy::IPC; | 254 request.message.transport_strategy = IPCBlobItemRequestStrategy::IPC; |
| 293 request.message.renderer_item_index = i; | 255 request.message.renderer_item_index = i; |
| 294 request.message.renderer_item_offset = 0; | 256 request.message.renderer_item_offset = 0; |
| 295 request.message.size = info.length(); | 257 request.message.size = info.length(); |
| 296 requests_.push_back(request); | 258 requests_.push_back(request); |
| 297 builder_->AppendFutureData(info.length()); | 259 builder->AppendFutureData(info.length()); |
| 298 } | 260 } |
| 299 } | 261 } |
| 300 | 262 |
| 301 /* static */ | 263 /* static */ |
| 302 bool BlobAsyncTransportStrategy::ShouldBeShortcut( | 264 bool BlobAsyncTransportRequestBuilder::ShouldBeShortcut( |
| 303 const std::vector<DataElement>& elements, | 265 const std::vector<DataElement>& elements, |
| 304 size_t memory_available) { | 266 size_t memory_available) { |
| 305 base::CheckedNumeric<size_t> shortcut_bytes = 0; | 267 base::CheckedNumeric<size_t> shortcut_bytes = 0; |
| 306 for (const auto& element : elements) { | 268 for (const auto& element : elements) { |
| 307 DataElement::Type type = element.type(); | 269 DataElement::Type type = element.type(); |
| 308 if (type == DataElement::TYPE_BYTES_DESCRIPTION) { | 270 if (type == DataElement::TYPE_BYTES_DESCRIPTION) { |
| 309 return false; | 271 return false; |
| 310 } | 272 } |
| 311 if (type == DataElement::TYPE_BYTES) { | 273 if (type == DataElement::TYPE_BYTES) { |
| 312 shortcut_bytes += element.length(); | 274 shortcut_bytes += element.length(); |
| 313 if (!shortcut_bytes.IsValid()) { | 275 if (!shortcut_bytes.IsValid()) { |
| 314 return false; | 276 return false; |
| 315 } | 277 } |
| 316 } | 278 } |
| 317 } | 279 } |
| 318 return shortcut_bytes.ValueOrDie() <= memory_available; | 280 return shortcut_bytes.ValueOrDie() <= memory_available; |
| 319 } | 281 } |
| 320 | 282 |
| 321 /* static */ | 283 /* static */ |
| 322 void BlobAsyncTransportStrategy::ComputeHandleSizes( | 284 void BlobAsyncTransportRequestBuilder::ComputeHandleSizes( |
| 323 uint64_t total_memory_size, | 285 uint64_t total_memory_size, |
| 324 size_t max_segment_size, | 286 size_t max_segment_size, |
| 325 std::vector<size_t>* segment_sizes) { | 287 std::vector<size_t>* segment_sizes) { |
| 326 size_t total_max_segments = | 288 size_t total_max_segments = |
| 327 static_cast<size_t>(total_memory_size / max_segment_size); | 289 static_cast<size_t>(total_memory_size / max_segment_size); |
| 328 bool has_extra_segment = (total_memory_size % max_segment_size) > 0; | 290 bool has_extra_segment = (total_memory_size % max_segment_size) > 0; |
| 329 segment_sizes->reserve(total_max_segments + (has_extra_segment ? 1 : 0)); | 291 segment_sizes->reserve(total_max_segments + (has_extra_segment ? 1 : 0)); |
| 330 segment_sizes->insert(segment_sizes->begin(), total_max_segments, | 292 segment_sizes->insert(segment_sizes->begin(), total_max_segments, |
| 331 max_segment_size); | 293 max_segment_size); |
| 332 if (has_extra_segment) { | 294 if (has_extra_segment) { |
| 333 segment_sizes->push_back(total_memory_size % max_segment_size); | 295 segment_sizes->push_back(total_memory_size % max_segment_size); |
| 334 } | 296 } |
| 335 } | 297 } |
| 336 | 298 |
| 337 } // namespace storage | 299 } // namespace storage |
| OLD | NEW |