Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "storage/browser/blob/blob_flattener.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <limits> | |
| 9 #include <set> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "storage/browser/blob/blob_async_transport_request_builder.h" | |
| 13 #include "storage/browser/blob/blob_data_builder.h" | |
| 14 #include "storage/browser/blob/blob_data_item.h" | |
| 15 #include "storage/browser/blob/blob_storage_context.h" | |
| 16 #include "storage/browser/blob/blob_storage_registry.h" | |
| 17 #include "storage/browser/blob/internal_blob_data.h" | |
| 18 #include "storage/browser/blob/shareable_blob_data_item.h" | |
| 19 #include "storage/common/data_element.h" | |
| 20 | |
| 21 namespace storage { | |
| 22 namespace { | |
| 23 using ItemCopyEntry = InternalBlobData::ItemCopyEntry; | |
| 24 | |
| 25 bool IsBytes(DataElement::Type type) { | |
| 26 return type == DataElement::TYPE_BYTES || | |
| 27 type == DataElement::TYPE_BYTES_DESCRIPTION; | |
| 28 } | |
| 29 } // namespace | |
| 30 | |
| 31 // static | |
| 32 BlobFlattener::BlobFlattener(const BlobDataBuilder& input_builder, | |
| 33 InternalBlobData* output_blob, | |
| 34 BlobStorageRegistry* registry) { | |
| 35 const std::string& uuid = input_builder.uuid_; | |
| 36 std::set<std::string> dependent_blob_uuids; | |
| 37 bool contains_pending_content = false; | |
| 38 | |
| 39 size_t num_files_with_unknown_size = 0; | |
| 40 | |
| 41 for (scoped_refptr<BlobDataItem> input_item : input_builder.items_) { | |
| 42 const DataElement& input_element = input_item->data_element(); | |
| 43 DataElement::Type type = input_element.type(); | |
| 44 uint64_t length = input_element.length(); | |
| 45 contains_pending_content |= type == DataElement::TYPE_BYTES_DESCRIPTION; | |
| 46 | |
| 47 if (IsBytes(type)) { | |
| 48 DCHECK_NE(0 + DataElement::kUnknownSize, input_element.length()); | |
| 49 transport_quota_needed += length; | |
| 50 total_size += length; | |
| 51 scoped_refptr<ShareableBlobDataItem> item = new ShareableBlobDataItem( | |
| 52 std::move(input_item), ShareableBlobDataItem::QUOTA_NEEDED); | |
| 53 transport_pending_items.push_back(item.get()); | |
| 54 output_blob->AppendSharedBlobItem(uuid, std::move(item)); | |
| 55 continue; | |
| 56 } | |
| 57 if (type == DataElement::TYPE_BLOB) { | |
| 58 InternalBlobData* ref_entry = | |
| 59 registry->GetEntry(input_element.blob_uuid()); | |
| 60 | |
| 61 if (!ref_entry || input_element.blob_uuid() == uuid) { | |
| 62 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 63 return; | |
| 64 } | |
| 65 | |
| 66 if (BlobStatusIsError(ref_entry->status())) { | |
| 67 status = BlobStatus::ERR_REFERENCED_BLOB_BROKEN; | |
| 68 return; | |
| 69 } | |
| 70 | |
| 71 if (ref_entry->total_size() == DataElement::kUnknownSize) { | |
| 72 // We can't reference a blob with unknown size. | |
| 73 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 74 return; | |
| 75 } | |
| 76 | |
| 77 if (dependent_blob_uuids.find(input_element.blob_uuid()) == | |
| 78 dependent_blob_uuids.end()) { | |
| 79 dependent_blobs.push_back( | |
| 80 std::make_pair(input_element.blob_uuid(), ref_entry)); | |
| 81 dependent_blob_uuids.insert(input_element.blob_uuid()); | |
| 82 } | |
| 83 | |
| 84 length = length == DataElement::kUnknownSize ? ref_entry->total_size() | |
| 85 : input_element.length(); | |
| 86 total_size += length; | |
| 87 | |
| 88 // If we're referencing the whole blob, then we don't need to slice. | |
| 89 if (input_element.offset() == 0 && length == ref_entry->total_size()) { | |
| 90 for (const auto& shareable_item : ref_entry->items()) { | |
| 91 output_blob->AppendSharedBlobItem(uuid, shareable_item); | |
| 92 } | |
| 93 continue; | |
| 94 } | |
| 95 | |
| 96 // Validate our reference has good offset & length. | |
| 97 if (input_element.offset() + input_element.length() > | |
| 98 ref_entry->total_size()) { | |
| 99 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 100 return; | |
| 101 } | |
| 102 | |
| 103 BlobSlice slice(*ref_entry, input_element.offset(), | |
| 104 input_element.length()); | |
|
Marijn Kruisselbrink
2016/08/05 23:23:54
Should this use input_element.length() (which can
dmurph
2016/08/19 00:18:32
Good idea. Thanks.
| |
| 105 | |
| 106 if (slice.first_source_item) { | |
| 107 copies.push_back(ItemCopyEntry(slice.first_source_item, | |
| 108 slice.first_item_slice_offset, | |
| 109 slice.dest_items.front())); | |
| 110 copy_pending_items.push_back(slice.dest_items.front().get()); | |
| 111 } | |
| 112 if (slice.last_source_item) { | |
| 113 copies.push_back( | |
| 114 ItemCopyEntry(slice.last_source_item, 0, slice.dest_items.back())); | |
| 115 copy_pending_items.push_back(slice.dest_items.back().get()); | |
| 116 } | |
| 117 copy_quota_needed += slice.copying_memory_size; | |
| 118 | |
| 119 for (auto& shareable_item : slice.dest_items) { | |
| 120 output_blob->AppendSharedBlobItem(uuid, std::move(shareable_item)); | |
| 121 } | |
| 122 continue; | |
| 123 } | |
| 124 | |
| 125 // If the source item is a temporary file item, then we need to keep track | |
| 126 // of that and mark is as needing quota. | |
| 127 scoped_refptr<ShareableBlobDataItem> item; | |
| 128 if (type == DataElement::TYPE_FILE && | |
| 129 BlobDataBuilder::IsTemporaryFileItem(input_element)) { | |
| 130 item = new ShareableBlobDataItem(std::move(input_item), | |
| 131 ShareableBlobDataItem::QUOTA_NEEDED); | |
| 132 contains_pending_content = true; | |
| 133 transport_pending_items.push_back(item.get()); | |
| 134 transport_quota_needed += length; | |
| 135 } else { | |
| 136 item = new ShareableBlobDataItem( | |
| 137 std::move(input_item), | |
| 138 ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA); | |
| 139 } | |
| 140 if (length == DataElement::kUnknownSize) | |
| 141 num_files_with_unknown_size++; | |
| 142 | |
| 143 total_size += length; | |
|
Marijn Kruisselbrink
2016/08/05 23:23:54
It seems a bit odd to sometimes be adding kUnknown
dmurph
2016/08/19 00:18:32
Yeah, since total_size is a safe math variable any
| |
| 144 output_blob->AppendSharedBlobItem(uuid, std::move(item)); | |
| 145 } | |
| 146 | |
| 147 if (num_files_with_unknown_size > 1 && input_builder.items_.size() > 1) { | |
| 148 LOG(ERROR) << "We only allow an unknown size when it's a single file item"; | |
| 149 status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 150 return; | |
| 151 } | |
| 152 status = contains_pending_content ? BlobStatus::PENDING : BlobStatus::DONE; | |
|
Marijn Kruisselbrink
2016/08/05 23:23:54
Currently you have whoever called BlobFlattener ch
dmurph
2016/08/19 00:18:33
Done.
| |
| 153 } | |
| 154 | |
| 155 BlobFlattener::~BlobFlattener() {} | |
| 156 | |
| 157 BlobSlice::BlobSlice(const InternalBlobData& source, | |
| 158 uint64_t slice_offset, | |
| 159 uint64_t slice_size) { | |
| 160 const auto& source_items = source.items(); | |
| 161 const auto& offsets = source.offsets(); | |
| 162 LOG(ERROR) << "doing a slice at " << slice_offset << " with size " | |
| 163 << slice_size; | |
| 164 DCHECK_LE(slice_offset + slice_size, source.total_size()); | |
| 165 size_t item_index = | |
| 166 std::upper_bound(offsets.begin(), offsets.end(), slice_offset) - | |
| 167 offsets.begin(); | |
| 168 uint64_t item_offset = | |
| 169 item_index == 0 ? slice_offset : slice_offset - offsets[item_index - 1]; | |
| 170 size_t num_items = source_items.size(); | |
| 171 | |
| 172 size_t first_item_index = item_index; | |
| 173 copying_memory_size = 0; | |
| 174 | |
| 175 // Read starting from 'first_item_index' and 'item_offset'. | |
| 176 for (uint64_t total_sliced = 0; | |
| 177 item_index < num_items && total_sliced < slice_size; item_index++) { | |
| 178 const scoped_refptr<BlobDataItem>& source_item = | |
| 179 source_items[item_index]->item(); | |
| 180 uint64_t source_length = source_item->length(); | |
| 181 DCHECK_NE(source_length, std::numeric_limits<uint64_t>::max()); | |
| 182 DCHECK_NE(source_length, 0ull); | |
| 183 | |
| 184 uint64_t read_size = | |
| 185 std::min(source_length - item_offset, slice_size - total_sliced); | |
| 186 total_sliced += read_size; | |
| 187 | |
| 188 if (read_size == source_length) { | |
| 189 // We can share the entire item. | |
| 190 LOG(ERROR) << "we can share"; | |
| 191 dest_items.push_back(source_items[item_index]); | |
| 192 continue; | |
| 193 } | |
| 194 | |
| 195 scoped_refptr<BlobDataItem> data_item; | |
| 196 ShareableBlobDataItem::State state = | |
| 197 ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA; | |
| 198 switch (source_item->type()) { | |
| 199 case DataElement::TYPE_BYTES_DESCRIPTION: | |
| 200 case DataElement::TYPE_BYTES: { | |
| 201 if (item_index == first_item_index) { | |
| 202 first_item_slice_offset = item_offset; | |
| 203 first_source_item = source_items[item_index]; | |
| 204 } else { | |
| 205 last_source_item = source_items[item_index]; | |
| 206 } | |
| 207 LOG(ERROR) << "we're slicing a bytes item!"; | |
| 208 copying_memory_size += read_size; | |
| 209 // Since we don't have quota yet for memory, we create temporary items | |
| 210 // for this data. When our blob is finished constructing, all dependent | |
| 211 // blobs are done, and we have enough memory quota, we'll copy the data | |
| 212 // over. | |
| 213 std::unique_ptr<DataElement> element(new DataElement()); | |
| 214 element->SetToBytesDescription(base::checked_cast<size_t>(read_size)); | |
| 215 data_item = new BlobDataItem(std::move(element)); | |
| 216 state = ShareableBlobDataItem::QUOTA_NEEDED; | |
| 217 break; | |
| 218 } | |
| 219 case DataElement::TYPE_FILE: { | |
| 220 std::unique_ptr<DataElement> element(new DataElement()); | |
| 221 element->SetToFilePathRange( | |
| 222 source_item->path(), source_item->offset() + item_offset, read_size, | |
| 223 source_item->expected_modification_time()); | |
| 224 data_item = | |
| 225 new BlobDataItem(std::move(element), source_item->data_handle_); | |
| 226 | |
| 227 if (BlobDataBuilder::IsTemporaryFileItem(source_item->data_element())) { | |
| 228 // Since we don't have file path / reference for our future file, we | |
| 229 // create another file item with this temporary file name. When our | |
| 230 // blob is finished constructing, all dependent blobs are done, and we | |
| 231 // can copy the handle over. | |
| 232 LOG(ERROR) << "we're slicing a temp file item!"; | |
| 233 if (item_index == first_item_index) { | |
| 234 first_item_slice_offset = item_offset; | |
| 235 first_source_item = source_items[item_index]; | |
| 236 } else { | |
| 237 last_source_item = source_items[item_index]; | |
| 238 } | |
| 239 state = ShareableBlobDataItem::QUOTA_NEEDED; | |
| 240 } | |
| 241 break; | |
| 242 } | |
| 243 case DataElement::TYPE_FILE_FILESYSTEM: { | |
| 244 std::unique_ptr<DataElement> element(new DataElement()); | |
| 245 element->SetToFileSystemUrlRange( | |
| 246 source_item->filesystem_url(), source_item->offset() + item_offset, | |
| 247 read_size, source_item->expected_modification_time()); | |
| 248 data_item = new BlobDataItem(std::move(element)); | |
| 249 break; | |
| 250 } | |
| 251 case DataElement::TYPE_DISK_CACHE_ENTRY: { | |
| 252 std::unique_ptr<DataElement> element(new DataElement()); | |
| 253 element->SetToDiskCacheEntryRange(source_item->offset() + item_offset, | |
| 254 read_size); | |
| 255 data_item = | |
| 256 new BlobDataItem(std::move(element), source_item->data_handle_, | |
| 257 source_item->disk_cache_entry(), | |
| 258 source_item->disk_cache_stream_index(), | |
| 259 source_item->disk_cache_side_stream_index()); | |
| 260 break; | |
| 261 } | |
| 262 case DataElement::TYPE_BLOB: | |
| 263 case DataElement::TYPE_UNKNOWN: | |
| 264 CHECK(false) << "Illegal blob item type: " << source_item->type(); | |
| 265 } | |
| 266 dest_items.push_back( | |
| 267 new ShareableBlobDataItem(std::move(data_item), state)); | |
| 268 item_offset = 0; | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 BlobSlice::~BlobSlice() {} | |
| 273 | |
| 274 } // namespace storage | |
| OLD | NEW |