| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <stddef.h> | |
| 6 #include <stdint.h> | |
| 7 | |
| 8 #include <algorithm> | |
| 9 | |
| 10 #include "base/numerics/safe_math.h" | |
| 11 #include "storage/browser/blob/blob_async_transport_request_builder.h" | |
| 12 #include "storage/common/blob_storage/blob_storage_constants.h" | |
| 13 | |
| 14 namespace storage { | |
| 15 namespace { | |
| 16 bool IsBytes(DataElement::Type type) { | |
| 17 return type == DataElement::TYPE_BYTES || | |
| 18 type == DataElement::TYPE_BYTES_DESCRIPTION; | |
| 19 } | |
| 20 | |
| 21 // This is the general template that each strategy below implements. See the | |
| 22 // ForEachWithSegment method for a description of how these are called. | |
| 23 // class BlobSegmentVisitor { | |
| 24 // public: | |
| 25 // typedef ___ SizeType; | |
| 26 // void VisitBytesSegment(size_t element_index, uint64_t element_offset, | |
| 27 // size_t segment_index, uint64_t segment_offset, | |
| 28 // uint64_t size); | |
| 29 // void VisitNonBytesSegment(const DataElement& element, size_t element_idx); | |
| 30 // void Done(); | |
| 31 // }; | |
| 32 | |
| 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 | |
| 35 // is now packed into file chunks, and the browser items will just reference | |
| 36 // the file with offsets and sizes. | |
| 37 class FileStorageStrategy { | |
| 38 public: | |
| 39 FileStorageStrategy( | |
| 40 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* | |
| 41 requests, | |
| 42 BlobDataBuilder* builder) | |
| 43 : requests(requests), builder(builder), current_item_index(0) {} | |
| 44 | |
| 45 ~FileStorageStrategy() {} | |
| 46 | |
| 47 void VisitBytesSegment(size_t element_index, | |
| 48 uint64_t element_offset, | |
| 49 size_t segment_index, | |
| 50 uint64_t segment_offset, | |
| 51 uint64_t size) { | |
| 52 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; | |
| 53 request.browser_item_index = current_item_index; | |
| 54 request.browser_item_offset = 0; | |
| 55 request.message.request_number = requests->size(); | |
| 56 request.message.transport_strategy = IPCBlobItemRequestStrategy::FILE; | |
| 57 request.message.renderer_item_index = element_index; | |
| 58 request.message.renderer_item_offset = element_offset; | |
| 59 request.message.size = size; | |
| 60 request.message.handle_index = segment_index; | |
| 61 request.message.handle_offset = segment_offset; | |
| 62 | |
| 63 requests->push_back(request); | |
| 64 builder->AppendFutureFile(segment_offset, size, segment_index); | |
| 65 current_item_index++; | |
| 66 } | |
| 67 | |
| 68 void VisitNonBytesSegment(const DataElement& element, size_t element_index) { | |
| 69 builder->AppendIPCDataElement(element); | |
| 70 current_item_index++; | |
| 71 } | |
| 72 | |
| 73 void Done() {} | |
| 74 | |
| 75 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* | |
| 76 requests; | |
| 77 BlobDataBuilder* builder; | |
| 78 | |
| 79 size_t current_item_index; | |
| 80 }; | |
| 81 | |
| 82 // This class handles the logic of storing memory that is transported as | |
| 83 // consolidated shared memory. | |
| 84 class SharedMemoryStorageStrategy { | |
| 85 public: | |
| 86 SharedMemoryStorageStrategy( | |
| 87 size_t max_segment_size, | |
| 88 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* | |
| 89 requests, | |
| 90 BlobDataBuilder* builder) | |
| 91 : requests(requests), | |
| 92 max_segment_size(max_segment_size), | |
| 93 builder(builder), | |
| 94 current_item_size(0), | |
| 95 current_item_index(0) {} | |
| 96 ~SharedMemoryStorageStrategy() {} | |
| 97 | |
| 98 void VisitBytesSegment(size_t element_index, | |
| 99 uint64_t element_offset, | |
| 100 size_t segment_index, | |
| 101 uint64_t segment_offset, | |
| 102 uint64_t size) { | |
| 103 if (current_item_size + size > max_segment_size) { | |
| 104 builder->AppendFutureData(current_item_size); | |
| 105 current_item_index++; | |
| 106 current_item_size = 0; | |
| 107 } | |
| 108 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; | |
| 109 request.browser_item_index = current_item_index; | |
| 110 request.browser_item_offset = current_item_size; | |
| 111 request.message.request_number = requests->size(); | |
| 112 request.message.transport_strategy = | |
| 113 IPCBlobItemRequestStrategy::SHARED_MEMORY; | |
| 114 request.message.renderer_item_index = element_index; | |
| 115 request.message.renderer_item_offset = element_offset; | |
| 116 request.message.size = size; | |
| 117 request.message.handle_index = segment_index; | |
| 118 request.message.handle_offset = segment_offset; | |
| 119 | |
| 120 requests->push_back(request); | |
| 121 current_item_size += size; | |
| 122 } | |
| 123 | |
| 124 void VisitNonBytesSegment(const DataElement& element, size_t element_index) { | |
| 125 if (current_item_size != 0) { | |
| 126 builder->AppendFutureData(current_item_size); | |
| 127 current_item_index++; | |
| 128 } | |
| 129 builder->AppendIPCDataElement(element); | |
| 130 current_item_index++; | |
| 131 current_item_size = 0; | |
| 132 } | |
| 133 | |
| 134 void Done() { | |
| 135 if (current_item_size != 0) { | |
| 136 builder->AppendFutureData(current_item_size); | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 std::vector<BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest>* | |
| 141 requests; | |
| 142 | |
| 143 size_t max_segment_size; | |
| 144 BlobDataBuilder* builder; | |
| 145 size_t current_item_size; | |
| 146 uint64_t current_item_index; | |
| 147 }; | |
| 148 | |
| 149 // This iterates of the data elements and segments the 'bytes' data into | |
| 150 // the smallest number of segments given the max_segment_size. | |
| 151 // The callback describes either: | |
| 152 // * A non-memory item | |
| 153 // * A partition of a bytes element which will be populated into a given | |
| 154 // segment and segment offset. | |
| 155 // More specifically, we split each |element| into one or more |segments| of a | |
| 156 // max_size, invokes the strategy to determine the request to make for each | |
| 157 // |segment| produced. A |segment| can also span multiple |elements|. | |
| 158 // Assumptions: All memory items are consolidated. As in, there are no two | |
| 159 // 'bytes' items next to eachother. | |
| 160 template <typename Visitor> | |
| 161 void ForEachWithSegment(const std::vector<DataElement>& elements, | |
| 162 uint64_t max_segment_size, | |
| 163 Visitor* visitor) { | |
| 164 DCHECK_GT(max_segment_size, 0ull); | |
| 165 size_t segment_index = 0; | |
| 166 uint64_t segment_offset = 0; | |
| 167 size_t elements_length = elements.size(); | |
| 168 for (size_t element_index = 0; element_index < elements_length; | |
| 169 ++element_index) { | |
| 170 const auto& element = elements.at(element_index); | |
| 171 DataElement::Type type = element.type(); | |
| 172 if (!IsBytes(type)) { | |
| 173 visitor->VisitNonBytesSegment(element, element_index); | |
| 174 continue; | |
| 175 } | |
| 176 uint64_t element_memory_left = element.length(); | |
| 177 uint64_t element_offset = 0; | |
| 178 while (element_memory_left > 0) { | |
| 179 if (segment_offset == max_segment_size) { | |
| 180 ++segment_index; | |
| 181 segment_offset = 0; | |
| 182 } | |
| 183 uint64_t memory_writing = | |
| 184 std::min(max_segment_size - segment_offset, element_memory_left); | |
| 185 visitor->VisitBytesSegment(element_index, element_offset, segment_index, | |
| 186 segment_offset, memory_writing); | |
| 187 element_memory_left -= memory_writing; | |
| 188 segment_offset += memory_writing; | |
| 189 element_offset += memory_writing; | |
| 190 } | |
| 191 } | |
| 192 visitor->Done(); | |
| 193 } | |
| 194 } // namespace | |
| 195 | |
| 196 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest:: | |
| 197 RendererMemoryItemRequest() | |
| 198 : browser_item_index(0), browser_item_offset(0) {} | |
| 199 | |
| 200 BlobAsyncTransportRequestBuilder::BlobAsyncTransportRequestBuilder() | |
| 201 : total_bytes_size_(0) {} | |
| 202 | |
| 203 BlobAsyncTransportRequestBuilder::~BlobAsyncTransportRequestBuilder() {} | |
| 204 | |
| 205 // Initializes the transport strategy for file requests. | |
| 206 void BlobAsyncTransportRequestBuilder::InitializeForFileRequests( | |
| 207 size_t max_file_size, | |
| 208 uint64_t blob_total_size, | |
| 209 const std::vector<DataElement>& elements, | |
| 210 BlobDataBuilder* builder) { | |
| 211 DCHECK(requests_.empty()); | |
| 212 total_bytes_size_ = blob_total_size; | |
| 213 ComputeHandleSizes(total_bytes_size_, max_file_size, &file_sizes_); | |
| 214 FileStorageStrategy strategy(&requests_, builder); | |
| 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( | |
| 235 size_t max_ipc_memory_size, | |
| 236 uint64_t blob_total_size, | |
| 237 const std::vector<DataElement>& elements, | |
| 238 BlobDataBuilder* builder) { | |
| 239 DCHECK(requests_.empty()); | |
| 240 // We don't segment anything, and just request the memory items directly | |
| 241 // in IPC. | |
| 242 size_t items_length = elements.size(); | |
| 243 total_bytes_size_ = blob_total_size; | |
| 244 for (size_t i = 0; i < items_length; i++) { | |
| 245 const auto& info = elements.at(i); | |
| 246 if (!IsBytes(info.type())) { | |
| 247 builder->AppendIPCDataElement(info); | |
| 248 continue; | |
| 249 } | |
| 250 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest request; | |
| 251 request.browser_item_index = i; | |
| 252 request.browser_item_offset = 0; | |
| 253 request.message.request_number = requests_.size(); | |
| 254 request.message.transport_strategy = IPCBlobItemRequestStrategy::IPC; | |
| 255 request.message.renderer_item_index = i; | |
| 256 request.message.renderer_item_offset = 0; | |
| 257 request.message.size = info.length(); | |
| 258 requests_.push_back(request); | |
| 259 builder->AppendFutureData(info.length()); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 /* static */ | |
| 264 bool BlobAsyncTransportRequestBuilder::ShouldBeShortcut( | |
| 265 const std::vector<DataElement>& elements, | |
| 266 size_t memory_available) { | |
| 267 base::CheckedNumeric<size_t> shortcut_bytes = 0; | |
| 268 for (const auto& element : elements) { | |
| 269 DataElement::Type type = element.type(); | |
| 270 if (type == DataElement::TYPE_BYTES_DESCRIPTION) { | |
| 271 return false; | |
| 272 } | |
| 273 if (type == DataElement::TYPE_BYTES) { | |
| 274 shortcut_bytes += element.length(); | |
| 275 if (!shortcut_bytes.IsValid()) { | |
| 276 return false; | |
| 277 } | |
| 278 } | |
| 279 } | |
| 280 return shortcut_bytes.ValueOrDie() <= memory_available; | |
| 281 } | |
| 282 | |
| 283 /* static */ | |
| 284 void BlobAsyncTransportRequestBuilder::ComputeHandleSizes( | |
| 285 uint64_t total_memory_size, | |
| 286 size_t max_segment_size, | |
| 287 std::vector<size_t>* segment_sizes) { | |
| 288 size_t total_max_segments = | |
| 289 static_cast<size_t>(total_memory_size / max_segment_size); | |
| 290 bool has_extra_segment = (total_memory_size % max_segment_size) > 0; | |
| 291 segment_sizes->reserve(total_max_segments + (has_extra_segment ? 1 : 0)); | |
| 292 segment_sizes->insert(segment_sizes->begin(), total_max_segments, | |
| 293 max_segment_size); | |
| 294 if (has_extra_segment) { | |
| 295 segment_sizes->push_back(total_memory_size % max_segment_size); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 } // namespace storage | |
| OLD | NEW |