OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 "storage/browser/blob/blob_storage_context.h" | 5 #include "storage/browser/blob/blob_storage_context.h" |
6 | 6 |
7 #include <algorithm> | |
8 #include <limits> | |
9 | |
7 #include "base/bind.h" | 10 #include "base/bind.h" |
8 #include "base/location.h" | 11 #include "base/location.h" |
9 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/memory/scoped_ptr.h" | |
10 #include "base/message_loop/message_loop_proxy.h" | 14 #include "base/message_loop/message_loop_proxy.h" |
11 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
12 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
13 #include "storage/browser/blob/blob_data_builder.h" | 17 #include "storage/browser/blob/blob_data_builder.h" |
18 #include "storage/browser/blob/blob_data_handle.h" | |
14 #include "url/gurl.h" | 19 #include "url/gurl.h" |
15 | 20 |
16 namespace storage { | 21 namespace storage { |
17 | 22 |
18 namespace { | 23 namespace { |
19 | 24 |
20 // We can't use GURL directly for these hash fragment manipulations | 25 // We can't use GURL directly for these hash fragment manipulations |
21 // since it doesn't have specific knowlege of the BlobURL format. GURL | 26 // since it doesn't have specific knowlege of the BlobURL format. GURL |
22 // treats BlobURLs as if they were PathURLs which don't support hash | 27 // treats BlobURLs as if they were PathURLs which don't support hash |
23 // fragments. | 28 // fragments. |
24 | 29 |
25 bool BlobUrlHasRef(const GURL& url) { | 30 bool BlobUrlHasRef(const GURL& url) { |
26 return url.spec().find('#') != std::string::npos; | 31 return url.spec().find('#') != std::string::npos; |
27 } | 32 } |
28 | 33 |
29 GURL ClearBlobUrlRef(const GURL& url) { | 34 GURL ClearBlobUrlRef(const GURL& url) { |
30 size_t hash_pos = url.spec().find('#'); | 35 size_t hash_pos = url.spec().find('#'); |
31 if (hash_pos == std::string::npos) | 36 if (hash_pos == std::string::npos) |
32 return url; | 37 return url; |
33 return GURL(url.spec().substr(0, hash_pos)); | 38 return GURL(url.spec().substr(0, hash_pos)); |
34 } | 39 } |
35 | 40 |
36 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some | 41 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some |
37 // way to come up with a better limit. | 42 // way to come up with a better limit. |
38 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. | 43 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. |
39 | 44 |
40 } // namespace | 45 } // namespace |
41 | 46 |
42 BlobStorageContext::BlobMapEntry::BlobMapEntry() | 47 BlobStorageContext::BlobMapEntry::BlobMapEntry() : refcount(0), flags(0) { |
43 : refcount(0), flags(0) { | |
44 } | 48 } |
45 | 49 |
46 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, | 50 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, |
47 BlobDataBuilder* data) | 51 InternalBlobData::Builder* data) |
48 : refcount(refcount), flags(0), data_builder(data) { | 52 : refcount(refcount), flags(0), data_builder(data) { |
49 } | 53 } |
50 | 54 |
51 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { | 55 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { |
52 } | 56 } |
53 | 57 |
54 bool BlobStorageContext::BlobMapEntry::IsBeingBuilt() { | 58 bool BlobStorageContext::BlobMapEntry::IsBeingBuilt() { |
55 return data_builder; | 59 return data_builder; |
56 } | 60 } |
57 | 61 |
58 BlobStorageContext::BlobStorageContext() | 62 BlobStorageContext::BlobStorageContext() : memory_usage_(0) { |
59 : memory_usage_(0) { | |
60 } | 63 } |
61 | 64 |
62 BlobStorageContext::~BlobStorageContext() { | 65 BlobStorageContext::~BlobStorageContext() { |
63 STLDeleteContainerPairSecondPointers(blob_map_.begin(), blob_map_.end()); | 66 STLDeleteContainerPairSecondPointers(blob_map_.begin(), blob_map_.end()); |
64 } | 67 } |
65 | 68 |
66 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( | 69 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( |
67 const std::string& uuid) { | 70 const std::string& uuid) { |
68 scoped_ptr<BlobDataHandle> result; | 71 scoped_ptr<BlobDataHandle> result; |
69 BlobMap::iterator found = blob_map_.find(uuid); | 72 BlobMap::iterator found = blob_map_.find(uuid); |
70 if (found == blob_map_.end()) | 73 if (found == blob_map_.end()) |
71 return result.Pass(); | 74 return result.Pass(); |
72 auto* entry = found->second; | 75 auto* entry = found->second; |
73 if (entry->flags & EXCEEDED_MEMORY) | 76 if (entry->flags & EXCEEDED_MEMORY) |
74 return result.Pass(); | 77 return result.Pass(); |
75 DCHECK(!entry->IsBeingBuilt()); | 78 DCHECK(!entry->IsBeingBuilt()); |
76 result.reset( | 79 result.reset( |
77 new BlobDataHandle(uuid, this, base::MessageLoopProxy::current().get())); | 80 new BlobDataHandle(uuid, this, base::MessageLoopProxy::current().get())); |
78 return result.Pass(); | 81 return result.Pass(); |
79 } | 82 } |
80 | 83 |
81 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( | 84 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( |
82 const GURL& url) { | 85 const GURL& url) { |
83 BlobURLMap::iterator found = public_blob_urls_.find( | 86 BlobURLMap::iterator found = |
84 BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); | 87 public_blob_urls_.find(BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); |
85 if (found == public_blob_urls_.end()) | 88 if (found == public_blob_urls_.end()) |
86 return scoped_ptr<BlobDataHandle>(); | 89 return scoped_ptr<BlobDataHandle>(); |
87 return GetBlobDataFromUUID(found->second); | 90 return GetBlobDataFromUUID(found->second); |
88 } | 91 } |
89 | 92 |
90 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( | 93 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( |
91 const BlobDataBuilder& data) { | 94 BlobDataBuilder* external_builder) { |
92 StartBuildingBlob(data.uuid_); | 95 StartBuildingBlob(external_builder->uuid_); |
93 for (const auto& blob_item : data.items_) | 96 BlobMap::iterator found = blob_map_.find(external_builder->uuid_); |
94 AppendBlobDataItem(data.uuid_, *(blob_item->item_)); | 97 DCHECK(found != blob_map_.end()); |
95 FinishBuildingBlob(data.uuid_, data.content_type_); | 98 BlobMapEntry* entry = found->second; |
96 scoped_ptr<BlobDataHandle> handle = GetBlobDataFromUUID(data.uuid_); | 99 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); |
97 DecrementBlobRefCount(data.uuid_); | 100 DCHECK(target_blob_builder); |
101 | |
102 target_blob_builder->set_content_disposition( | |
103 external_builder->content_disposition_); | |
104 for (const auto& blob_item : external_builder->items_) { | |
105 if (!AppendAllocatedBlobItem(external_builder->uuid_, target_blob_builder, | |
106 blob_item)) { | |
107 BlobEntryExceededMemory(entry); | |
108 break; | |
109 } | |
110 } | |
111 | |
112 FinishBuildingBlob(external_builder->uuid_, external_builder->content_type_); | |
113 scoped_ptr<BlobDataHandle> handle = | |
114 GetBlobDataFromUUID(external_builder->uuid_); | |
115 DecrementBlobRefCount(external_builder->uuid_); | |
98 return handle.Pass(); | 116 return handle.Pass(); |
99 } | 117 } |
100 | 118 |
101 bool BlobStorageContext::RegisterPublicBlobURL( | 119 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, |
102 const GURL& blob_url, const std::string& uuid) { | 120 const std::string& uuid) { |
103 DCHECK(!BlobUrlHasRef(blob_url)); | 121 DCHECK(!BlobUrlHasRef(blob_url)); |
104 DCHECK(IsInUse(uuid)); | 122 DCHECK(IsInUse(uuid)); |
105 DCHECK(!IsUrlRegistered(blob_url)); | 123 DCHECK(!IsUrlRegistered(blob_url)); |
106 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) | 124 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) |
107 return false; | 125 return false; |
108 IncrementBlobRefCount(uuid); | 126 IncrementBlobRefCount(uuid); |
109 public_blob_urls_[blob_url] = uuid; | 127 public_blob_urls_[blob_url] = uuid; |
110 return true; | 128 return true; |
111 } | 129 } |
112 | 130 |
113 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { | 131 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { |
114 DCHECK(!BlobUrlHasRef(blob_url)); | 132 DCHECK(!BlobUrlHasRef(blob_url)); |
115 if (!IsUrlRegistered(blob_url)) | 133 if (!IsUrlRegistered(blob_url)) |
116 return; | 134 return; |
117 DecrementBlobRefCount(public_blob_urls_[blob_url]); | 135 DecrementBlobRefCount(public_blob_urls_[blob_url]); |
118 public_blob_urls_.erase(blob_url); | 136 public_blob_urls_.erase(blob_url); |
119 } | 137 } |
120 | 138 |
121 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( | 139 scoped_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot( |
122 const std::string& uuid) { | 140 const std::string& uuid) { |
123 scoped_ptr<BlobDataSnapshot> result; | 141 scoped_ptr<BlobDataSnapshot> result; |
124 auto found = blob_map_.find(uuid); | 142 auto found = blob_map_.find(uuid); |
125 DCHECK(found != blob_map_.end()) | 143 DCHECK(found != blob_map_.end()) |
126 << "Blob should be in map, as the handle is still around"; | 144 << "Blob " << uuid << " should be in map, as the handle is still around"; |
127 BlobMapEntry* entry = found->second; | 145 BlobMapEntry* entry = found->second; |
128 DCHECK(!entry->IsBeingBuilt()); | 146 DCHECK(!entry->IsBeingBuilt()); |
129 result.reset(new BlobDataSnapshot(*entry->data)); | 147 const InternalBlobData& data = *entry->data; |
130 return result.Pass(); | 148 |
149 scoped_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot( | |
150 uuid, data.content_type(), data.content_disposition())); | |
151 snapshot->items_.reserve(data.items().size()); | |
152 for (const auto& shareable_item : data.items()) { | |
153 snapshot->items_.push_back(shareable_item->item()); | |
154 } | |
155 return snapshot; | |
131 } | 156 } |
132 | 157 |
133 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { | 158 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { |
134 DCHECK(!IsInUse(uuid) && !uuid.empty()); | 159 DCHECK(!IsInUse(uuid) && !uuid.empty()); |
135 blob_map_[uuid] = new BlobMapEntry(1, new BlobDataBuilder(uuid)); | 160 blob_map_[uuid] = new BlobMapEntry(1, new InternalBlobData::Builder()); |
136 } | 161 } |
137 | 162 |
138 void BlobStorageContext::AppendBlobDataItem(const std::string& uuid, | 163 void BlobStorageContext::AppendBlobDataItem( |
139 const DataElement& item) { | 164 const std::string& uuid, |
165 const storage::DataElement& ipc_data_element) { | |
140 DCHECK(IsBeingBuilt(uuid)); | 166 DCHECK(IsBeingBuilt(uuid)); |
141 BlobMap::iterator found = blob_map_.find(uuid); | 167 BlobMap::iterator found = blob_map_.find(uuid); |
142 if (found == blob_map_.end()) | 168 if (found == blob_map_.end()) |
143 return; | 169 return; |
144 BlobMapEntry* entry = found->second; | 170 BlobMapEntry* entry = found->second; |
145 if (entry->flags & EXCEEDED_MEMORY) | 171 if (entry->flags & EXCEEDED_MEMORY) |
146 return; | 172 return; |
147 BlobDataBuilder* target_blob_data = entry->data_builder.get(); | 173 InternalBlobData::Builder* target_blob_builder = entry->data_builder.get(); |
148 DCHECK(target_blob_data); | 174 DCHECK(target_blob_builder); |
149 | 175 |
150 bool exceeded_memory = false; | 176 if (ipc_data_element.type() == DataElement::TYPE_BYTES && |
151 | 177 memory_usage_ + ipc_data_element.length() > kMaxMemoryUsage) { |
152 // The blob data is stored in the canonical way which only contains a | 178 BlobEntryExceededMemory(entry); |
153 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items | 179 return; |
154 // are expanded into the primitive constituent types. | |
155 // 1) The Data item is denoted by the raw data and length. | |
156 // 2) The File item is denoted by the file path, the range and the expected | |
157 // modification time. | |
158 // 3) The FileSystem File item is denoted by the FileSystem URL, the range | |
159 // and the expected modification time. | |
160 // 4) The Blob items are expanded. | |
161 // TODO(michaeln): Would be nice to avoid copying Data items when expanding. | |
162 | |
163 uint64 length = item.length(); | |
164 DCHECK_GT(length, 0u); | |
165 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", | |
166 memory_usage_ / 1024); | |
167 switch (item.type()) { | |
168 case DataElement::TYPE_BYTES: | |
169 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); | |
170 DCHECK(!item.offset()); | |
171 exceeded_memory = !AppendBytesItem(target_blob_data, item.bytes(), | |
172 static_cast<int64>(length)); | |
173 break; | |
174 case DataElement::TYPE_FILE: | |
175 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.File", length / 1024); | |
176 AppendFileItem(target_blob_data, item.path(), item.offset(), | |
177 item.length(), item.expected_modification_time()); | |
178 break; | |
179 case DataElement::TYPE_FILE_FILESYSTEM: | |
180 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.FileSystem", length / 1024); | |
181 AppendFileSystemFileItem(target_blob_data, item.filesystem_url(), | |
182 item.offset(), item.length(), | |
183 item.expected_modification_time()); | |
184 break; | |
185 case DataElement::TYPE_BLOB: { | |
186 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", length / 1024); | |
187 // We grab the handle to ensure it stays around while we copy it. | |
188 scoped_ptr<BlobDataHandle> src = GetBlobDataFromUUID(item.blob_uuid()); | |
189 if (src) { | |
190 BlobMapEntry* entry = blob_map_.find(item.blob_uuid())->second; | |
191 DCHECK(entry->data); | |
192 exceeded_memory = !ExpandStorageItems(target_blob_data, *entry->data, | |
193 item.offset(), item.length()); | |
194 } | |
195 break; | |
196 } | |
197 default: | |
198 NOTREACHED(); | |
199 break; | |
200 } | 180 } |
201 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", | 181 if (!AppendAllocatedBlobItem(uuid, target_blob_builder, |
202 memory_usage_ / 1024); | 182 AllocateBlobItem(uuid, ipc_data_element))) { |
203 | 183 BlobEntryExceededMemory(entry); |
204 // If we're using too much memory, drop this blob's data. | |
205 // TODO(michaeln): Blob memory storage does not yet spill over to disk, | |
206 // as a stop gap, we'll prevent memory usage over a max amount. | |
207 if (exceeded_memory) { | |
208 memory_usage_ -= target_blob_data->GetMemoryUsage(); | |
209 entry->flags |= EXCEEDED_MEMORY; | |
210 entry->data_builder.reset(new BlobDataBuilder(uuid)); | |
211 return; | |
212 } | 184 } |
213 } | 185 } |
214 | 186 |
215 void BlobStorageContext::FinishBuildingBlob( | 187 void BlobStorageContext::FinishBuildingBlob(const std::string& uuid, |
216 const std::string& uuid, const std::string& content_type) { | 188 const std::string& content_type) { |
217 DCHECK(IsBeingBuilt(uuid)); | 189 DCHECK(IsBeingBuilt(uuid)); |
218 BlobMap::iterator found = blob_map_.find(uuid); | 190 BlobMap::iterator found = blob_map_.find(uuid); |
219 if (found == blob_map_.end()) | 191 if (found == blob_map_.end()) |
220 return; | 192 return; |
221 BlobMapEntry* entry = found->second; | 193 BlobMapEntry* entry = found->second; |
222 entry->data_builder->set_content_type(content_type); | 194 entry->data_builder->set_content_type(content_type); |
223 entry->data = entry->data_builder->BuildSnapshot().Pass(); | 195 entry->data = entry->data_builder->Build(); |
224 entry->data_builder.reset(); | 196 entry->data_builder.reset(); |
225 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); | 197 UMA_HISTOGRAM_COUNTS("Storage.Blob.ItemCount", entry->data->items().size()); |
226 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ExceededMemory", | 198 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ExceededMemory", |
227 (entry->flags & EXCEEDED_MEMORY) == EXCEEDED_MEMORY); | 199 (entry->flags & EXCEEDED_MEMORY) == EXCEEDED_MEMORY); |
200 UMA_HISTOGRAM_COUNTS("Storage.Blob.TotalSize", | |
201 entry->data->GetTotalMemoryUsage() / 1024); | |
228 } | 202 } |
229 | 203 |
230 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { | 204 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { |
231 DCHECK(IsBeingBuilt(uuid)); | 205 DCHECK(IsBeingBuilt(uuid)); |
232 DecrementBlobRefCount(uuid); | 206 DecrementBlobRefCount(uuid); |
233 } | 207 } |
234 | 208 |
235 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { | 209 void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) { |
236 BlobMap::iterator found = blob_map_.find(uuid); | 210 BlobMap::iterator found = blob_map_.find(uuid); |
237 if (found == blob_map_.end()) { | 211 if (found == blob_map_.end()) { |
238 DCHECK(false); | 212 DCHECK(false); |
239 return; | 213 return; |
240 } | 214 } |
241 ++(found->second->refcount); | 215 ++(found->second->refcount); |
242 } | 216 } |
243 | 217 |
244 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { | 218 void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) { |
245 BlobMap::iterator found = blob_map_.find(uuid); | 219 BlobMap::iterator found = blob_map_.find(uuid); |
246 if (found == blob_map_.end()) | 220 if (found == blob_map_.end()) |
247 return; | 221 return; |
248 auto* entry = found->second; | 222 auto* entry = found->second; |
249 if (--(entry->refcount) == 0) { | 223 if (--(entry->refcount) == 0) { |
224 size_t memory_freeing = 0; | |
250 if (entry->IsBeingBuilt()) { | 225 if (entry->IsBeingBuilt()) { |
251 memory_usage_ -= entry->data_builder->GetMemoryUsage(); | 226 memory_freeing = entry->data_builder->GetNonsharedMemoryUsage(); |
227 entry->data_builder->RemoveBlobFromShareableItems(uuid); | |
252 } else { | 228 } else { |
253 memory_usage_ -= entry->data->GetMemoryUsage(); | 229 memory_freeing = entry->data->GetNonsharedMemoryUsage(); |
230 entry->data->RemoveBlobFromShareableItems(uuid); | |
254 } | 231 } |
232 DCHECK_LE(memory_freeing, memory_usage_); | |
233 memory_usage_ -= memory_freeing; | |
255 delete entry; | 234 delete entry; |
256 blob_map_.erase(found); | 235 blob_map_.erase(found); |
257 } | 236 } |
258 } | 237 } |
259 | 238 |
260 bool BlobStorageContext::ExpandStorageItems( | 239 void BlobStorageContext::BlobEntryExceededMemory(BlobMapEntry* entry) { |
261 BlobDataBuilder* target_blob_data, | 240 // If we're using too much memory, drop this blob's data. |
262 const BlobDataSnapshot& src_blob_data, | 241 // TODO(michaeln): Blob memory storage does not yet spill over to disk, |
263 uint64 offset, | 242 // as a stop gap, we'll prevent memory usage over a max amount. |
264 uint64 length) { | 243 memory_usage_ -= entry->data_builder->GetNonsharedMemoryUsage(); |
265 DCHECK(target_blob_data && length != static_cast<uint64>(-1)); | 244 entry->flags |= EXCEEDED_MEMORY; |
245 entry->data_builder.reset(new InternalBlobData::Builder()); | |
246 } | |
266 | 247 |
267 const std::vector<scoped_refptr<BlobDataItem>>& items = src_blob_data.items(); | 248 scoped_refptr<BlobDataItem> BlobStorageContext::AllocateBlobItem( |
249 const std::string& uuid, | |
250 const DataElement& ipc_data) { | |
251 scoped_refptr<BlobDataItem> blob_item; | |
252 | |
253 uint64 length = ipc_data.length(); | |
254 scoped_ptr<DataElement> element(new DataElement()); | |
255 switch (ipc_data.type()) { | |
256 case DataElement::TYPE_BYTES: | |
257 DCHECK(!ipc_data.offset()); | |
258 element->SetToBytes(ipc_data.bytes(), length); | |
259 blob_item = new BlobDataItem(element.Pass()); | |
260 break; | |
261 case DataElement::TYPE_FILE: | |
262 element->SetToFilePathRange(ipc_data.path(), ipc_data.offset(), length, | |
263 ipc_data.expected_modification_time()); | |
264 blob_item = new BlobDataItem( | |
265 element.Pass(), ShareableFileReference::Get(ipc_data.path())); | |
266 break; | |
267 case DataElement::TYPE_FILE_FILESYSTEM: | |
268 element->SetToFileSystemUrlRange(ipc_data.filesystem_url(), | |
269 ipc_data.offset(), length, | |
270 ipc_data.expected_modification_time()); | |
271 blob_item = new BlobDataItem(element.Pass()); | |
272 break; | |
273 case DataElement::TYPE_BLOB: | |
274 // This is a temporary item that will be deconstructed later. | |
275 element->SetToBlobRange(ipc_data.blob_uuid(), ipc_data.offset(), | |
276 ipc_data.length()); | |
277 blob_item = new BlobDataItem(element.Pass()); | |
278 break; | |
279 default: | |
280 NOTREACHED(); | |
281 break; | |
282 } | |
283 | |
284 return blob_item; | |
285 } | |
286 | |
287 bool BlobStorageContext::AppendAllocatedBlobItem( | |
288 const std::string& target_blob_uuid, | |
289 InternalBlobData::Builder* target_blob_builder, | |
290 scoped_refptr<BlobDataItem> blob_item) { | |
291 bool exceeded_memory = false; | |
292 | |
293 // The blob data is stored in the canonical way which only contains a | |
294 // list of Data, File, and FileSystem items. Aggregated TYPE_BLOB items | |
295 // are expanded into the primitive constituent types. | |
296 // 1) The Data item is denoted by the raw data and length. | |
297 // 2) The File item is denoted by the file path, the range and the expected | |
298 // modification time. | |
299 // 3) The FileSystem File item is denoted by the FileSystem URL, the range | |
300 // and the expected modification time. | |
301 // 4) The Blob items are expanded. | |
michaeln
2015/02/09 22:31:40
consider retaining the term 'Expand' in the method
dmurph
2015/02/10 01:36:31
Done.
| |
302 | |
303 const DataElement& data_element = blob_item->data_element(); | |
304 uint64 length = data_element.length(); | |
305 uint64 offset = data_element.offset(); | |
306 DCHECK_GT(length, 0u); | |
307 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeBeforeAppend", | |
308 memory_usage_ / 1024); | |
309 switch (data_element.type()) { | |
310 case DataElement::TYPE_BYTES: | |
311 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Bytes", length / 1024); | |
312 DCHECK(!offset); | |
313 if (memory_usage_ + length > kMaxMemoryUsage) { | |
314 exceeded_memory = true; | |
315 break; | |
316 } | |
317 memory_usage_ += length; | |
318 target_blob_builder->AppendSharedBlobItem( | |
319 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
320 break; | |
321 case DataElement::TYPE_FILE: { | |
322 bool full_file = (length == std::numeric_limits<uint64>::max()); | |
323 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.File.Unknown", full_file); | |
324 if (!full_file) { | |
325 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.File", | |
326 (length - offset) / 1024); | |
327 } | |
328 target_blob_builder->AppendSharedBlobItem( | |
329 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
330 break; | |
331 } | |
332 case DataElement::TYPE_FILE_FILESYSTEM: { | |
333 bool full_file = (length == std::numeric_limits<uint64>::max()); | |
334 UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.FileSystem.Unknown", | |
335 full_file); | |
336 if (!full_file) { | |
337 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.FileSystem", | |
338 (length - offset) / 1024); | |
339 } | |
340 target_blob_builder->AppendSharedBlobItem( | |
341 new ShareableBlobDataItem(target_blob_uuid, blob_item)); | |
342 break; | |
343 } | |
344 case DataElement::TYPE_BLOB: { | |
345 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.Blob", | |
346 (length - offset) / 1024); | |
347 // We grab the handle to ensure it stays around while we copy it. | |
348 scoped_ptr<BlobDataHandle> src = | |
349 GetBlobDataFromUUID(data_element.blob_uuid()); | |
350 if (src) { | |
351 BlobMapEntry* other_entry = | |
352 blob_map_.find(data_element.blob_uuid())->second; | |
353 DCHECK(other_entry->data); | |
354 exceeded_memory = !AppendBlob(target_blob_uuid, target_blob_builder, | |
355 *other_entry->data, offset, length); | |
356 } | |
357 break; | |
358 } | |
359 default: | |
360 NOTREACHED(); | |
361 break; | |
362 } | |
363 UMA_HISTOGRAM_COUNTS("Storage.Blob.StorageSizeAfterAppend", | |
364 memory_usage_ / 1024); | |
365 | |
366 return !exceeded_memory; | |
367 } | |
368 | |
369 bool BlobStorageContext::AppendBlob( | |
370 const std::string& target_blob_uuid, | |
371 InternalBlobData::Builder* target_blob_builder, | |
372 const InternalBlobData& blob, | |
373 size_t offset, | |
374 size_t length) { | |
375 DCHECK(length > 0); | |
376 | |
377 const std::vector<scoped_refptr<ShareableBlobDataItem>>& items = blob.items(); | |
268 auto iter = items.begin(); | 378 auto iter = items.begin(); |
269 if (offset) { | 379 if (offset) { |
270 for (; iter != items.end(); ++iter) { | 380 for (; iter != items.end(); ++iter) { |
271 const BlobDataItem& item = *(iter->get()); | 381 const BlobDataItem& item = *(iter->get()->item()); |
272 if (offset >= item.length()) | 382 if (offset >= item.length()) |
273 offset -= item.length(); | 383 offset -= item.length(); |
274 else | 384 else |
275 break; | 385 break; |
276 } | 386 } |
277 } | 387 } |
278 | 388 |
279 for (; iter != items.end() && length > 0; ++iter) { | 389 for (; iter != items.end() && length > 0; ++iter) { |
280 const BlobDataItem& item = *(iter->get()); | 390 scoped_refptr<ShareableBlobDataItem> shareable_item = iter->get(); |
281 uint64 current_length = item.length() - offset; | 391 const BlobDataItem& item = *(shareable_item->item()); |
282 uint64 new_length = current_length > length ? length : current_length; | 392 size_t item_length = item.length(); |
283 if (iter->get()->type() == DataElement::TYPE_BYTES) { | 393 DCHECK_GT(item_length, offset); |
284 if (!AppendBytesItem( | 394 size_t current_length = item_length - offset; |
285 target_blob_data, | 395 size_t new_length = current_length > length ? length : current_length; |
286 item.bytes() + static_cast<size_t>(item.offset() + offset), | 396 |
287 static_cast<int64>(new_length))) { | 397 bool reusing_blob_item = offset == 0 && new_length == item.length(); |
288 return false; // exceeded memory | 398 UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ReusedItem", reusing_blob_item); |
289 } | 399 if (reusing_blob_item) { |
290 } else if (item.type() == DataElement::TYPE_FILE) { | 400 shareable_item->referencing_blobs().insert(target_blob_uuid); |
291 AppendFileItem(target_blob_data, item.path(), item.offset() + offset, | 401 target_blob_builder->AppendSharedBlobItem(shareable_item); |
292 new_length, item.expected_modification_time()); | 402 length -= new_length; |
293 } else { | 403 continue; |
294 DCHECK(item.type() == DataElement::TYPE_FILE_FILESYSTEM); | 404 } |
295 AppendFileSystemFileItem(target_blob_data, item.filesystem_url(), | 405 |
296 item.offset() + offset, new_length, | 406 // We need to do copying of the items when we have a different offset or |
297 item.expected_modification_time()); | 407 // length |
408 switch (item.type()) { | |
409 case DataElement::TYPE_BYTES: { | |
410 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.Bytes", | |
411 new_length / 1024); | |
412 if (memory_usage_ + new_length > kMaxMemoryUsage) { | |
413 return false; | |
414 } | |
415 DCHECK(!item.offset()); | |
416 scoped_ptr<DataElement> element(new DataElement()); | |
417 element->SetToBytes(item.bytes() + offset, | |
418 static_cast<int64>(new_length)); | |
419 memory_usage_ += new_length; | |
420 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
421 target_blob_uuid, new BlobDataItem(element.Pass()))); | |
422 } break; | |
423 case DataElement::TYPE_FILE: { | |
424 DCHECK_NE(item.length(), std::numeric_limits<uint64>::max()) | |
425 << "We cannot use a section of a file with an unknown length"; | |
426 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.File", | |
427 new_length / 1024); | |
428 scoped_ptr<DataElement> element(new DataElement()); | |
429 element->SetToFilePathRange(item.path(), item.offset() + offset, | |
430 new_length, | |
431 item.expected_modification_time()); | |
432 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
433 target_blob_uuid, | |
434 new BlobDataItem(element.Pass(), item.file_handle_))); | |
435 } break; | |
436 case DataElement::TYPE_FILE_FILESYSTEM: { | |
437 UMA_HISTOGRAM_COUNTS("Storage.BlobItemSize.BlobSlice.FileSystem", | |
438 new_length / 1024); | |
439 scoped_ptr<DataElement> element(new DataElement()); | |
440 element->SetToFileSystemUrlRange(item.filesystem_url(), | |
441 item.offset() + offset, new_length, | |
442 item.expected_modification_time()); | |
443 target_blob_builder->AppendSharedBlobItem(new ShareableBlobDataItem( | |
444 target_blob_uuid, new BlobDataItem(element.Pass()))); | |
445 } break; | |
446 default: | |
447 CHECK(false) << "Illegal blob item type: " << item.type(); | |
298 } | 448 } |
299 length -= new_length; | 449 length -= new_length; |
300 offset = 0; | 450 offset = 0; |
301 } | 451 } |
302 return true; | 452 return true; |
303 } | 453 } |
304 | 454 |
305 bool BlobStorageContext::AppendBytesItem(BlobDataBuilder* target_blob_data, | |
306 const char* bytes, | |
307 int64 length) { | |
308 if (length < 0) { | |
309 DCHECK(false); | |
310 return false; | |
311 } | |
312 if (memory_usage_ + length > kMaxMemoryUsage) { | |
313 return false; | |
314 } | |
315 target_blob_data->AppendData(bytes, static_cast<size_t>(length)); | |
316 memory_usage_ += length; | |
317 return true; | |
318 } | |
319 | |
320 void BlobStorageContext::AppendFileItem( | |
321 BlobDataBuilder* target_blob_data, | |
322 const base::FilePath& file_path, | |
323 uint64 offset, | |
324 uint64 length, | |
325 const base::Time& expected_modification_time) { | |
326 // It may be a temporary file that should be deleted when no longer needed. | |
327 scoped_refptr<ShareableFileReference> shareable_file = | |
328 ShareableFileReference::Get(file_path); | |
329 | |
330 target_blob_data->AppendFile(file_path, offset, length, | |
331 expected_modification_time, shareable_file); | |
332 } | |
333 | |
334 void BlobStorageContext::AppendFileSystemFileItem( | |
335 BlobDataBuilder* target_blob_data, | |
336 const GURL& filesystem_url, | |
337 uint64 offset, | |
338 uint64 length, | |
339 const base::Time& expected_modification_time) { | |
340 target_blob_data->AppendFileSystemFile(filesystem_url, offset, length, | |
341 expected_modification_time); | |
342 } | |
343 | |
344 bool BlobStorageContext::IsInUse(const std::string& uuid) { | 455 bool BlobStorageContext::IsInUse(const std::string& uuid) { |
345 return blob_map_.find(uuid) != blob_map_.end(); | 456 return blob_map_.find(uuid) != blob_map_.end(); |
346 } | 457 } |
347 | 458 |
348 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { | 459 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { |
349 BlobMap::iterator found = blob_map_.find(uuid); | 460 BlobMap::iterator found = blob_map_.find(uuid); |
350 if (found == blob_map_.end()) | 461 if (found == blob_map_.end()) |
351 return false; | 462 return false; |
352 return found->second->IsBeingBuilt(); | 463 return found->second->IsBeingBuilt(); |
353 } | 464 } |
354 | 465 |
355 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { | 466 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { |
356 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); | 467 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); |
357 } | 468 } |
358 | 469 |
359 } // namespace storage | 470 } // namespace storage |
OLD | NEW |