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 |