OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 "webkit/dom_storage/session_storage_database.h" | |
6 | |
7 #include "base/file_util.h" | |
8 #include "base/logging.h" | |
9 #include "base/stringprintf.h" | |
10 #include "base/string_number_conversions.h" | |
11 #include "base/utf_string_conversions.h" | |
12 #include "googleurl/src/gurl.h" | |
13 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
14 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | |
15 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
16 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
18 | |
19 // Layout of the database: | |
20 // | key | value | | |
21 // ----------------------------------------------------------------------- | |
22 // | map-1 | 2 (refcount, start of map-1-* keys)| | |
23 // | map-1-a | b (a = b in map 1) | | |
24 // | ... | | | |
25 // | namespace- | dummy (start of namespace-* keys) | | |
26 // | namespace-1 (1 = namespace id) | dummy (start of namespace-1-* keys)| | |
27 // | namespace-1-origin1 | 1 (mapid) | | |
28 // | namespace-1-origin2 | 2 | | |
29 // | namespace-2 | dummy | | |
30 // | namespace-2-origin1 | 1 (shallow copy) | | |
31 // | namespace-2-origin2 | 2 (shallow copy) | | |
32 // | namespace-3 | dummy | | |
33 // | namespace-3-origin1 | 3 (deep copy) | | |
34 // | namespace-3-origin2 | 2 (shallow copy) | | |
35 // | next-namespace-id | 4 | | |
36 // | next-map-id | 4 | | |
37 | |
38 namespace dom_storage { | |
39 | |
40 SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path) | |
michaeln
2012/04/25 08:32:12
Might be helpful to distinguish between namespace_
marja
2012/04/25 15:44:27
I tried to separate the db logic better from the h
| |
41 : file_path_(file_path), | |
42 db_error_(false), | |
43 is_inconsistent_(false), | |
44 namespace_offset_(0) { } | |
45 | |
46 SessionStorageDatabase::~SessionStorageDatabase() { | |
47 } | |
48 | |
49 void SessionStorageDatabase::ReadAllValues(int64 namespace_id, | |
50 const GURL& origin, | |
51 ValuesMap* result) { | |
52 // We don't create a database if it doesn't exist. In that case, there is | |
53 // nothing to be added to the result. | |
54 if (!LazyOpen(false)) | |
55 return; | |
56 // Check if there is map for |namespace_id| and |origin|. | |
57 std::string namespace_key = | |
58 NamespaceKey(namespace_id, namespace_offset_, origin); | |
59 std::string map_id; | |
60 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
61 if (s.IsNotFound()) | |
62 return; | |
63 if (!DatabaseErrorCheck(s.ok())) | |
64 return; | |
65 ReadMap(map_id, result, false); | |
michaeln
2012/04/25 08:32:12
pseudo code noodling...
if (!LazyOpen(false))
r
marja
2012/04/25 15:44:27
Done.
| |
66 } | |
67 | |
68 bool SessionStorageDatabase::CommitChanges(int64 namespace_id, | |
69 const GURL& origin, | |
70 bool clear_all_first, | |
71 const ValuesMap& changes) { | |
michaeln
2012/04/25 08:32:12
pseudo code noodling...
if (!LazyOpen(true))
re
marja
2012/04/25 15:44:27
This doesn't work, we cannot go and read the ref c
| |
72 // Even if |changes| is empty, we need to write the appropriate placeholders | |
73 // in the database, so that it can be later shallow-copied succssfully. | |
74 if (!LazyOpen(true)) | |
75 return false; | |
76 | |
77 leveldb::WriteBatch batch; | |
78 // Ensure that the keys "namespace-" "namespace-N" (see the schema above) | |
79 // exist. | |
80 batch.Put(NamespacePrefix(), ""); | |
81 batch.Put(NamespaceStartKey(namespace_id, namespace_offset_), ""); | |
michaeln
2012/04/25 08:32:12
would be nice to not overwrite existing entries fo
marja
2012/04/25 15:44:27
Done.
| |
82 | |
83 // Ensure that the next namespace id is up to date. | |
84 if (!UpdateNextNamespaceId(namespace_id, &batch)) | |
85 return false; | |
86 | |
87 // Write the data into the map. | |
88 std::string namespace_key = | |
89 NamespaceKey(namespace_id, namespace_offset_, origin); | |
90 std::string map_id; | |
91 leveldb::Status s; | |
92 s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
93 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
94 return false; | |
95 | |
96 if (s.ok()) { | |
97 // We shouldn't write data to a shallow copy. | |
michaeln
2012/04/25 08:32:12
should we dcheck this condition instead?
marja
2012/04/25 15:44:27
ConsistencyCheck DCHECKs.
ConsistencyCheck is the
| |
98 int64 ref_count; | |
99 if (!GetRefCount(MapRefCountKey(map_id), &ref_count)) | |
100 return false; | |
101 if (!ConsistencyCheck(ref_count == 1)) | |
102 return false; | |
103 | |
104 if (clear_all_first) { | |
105 if (!ClearMap(map_id, &batch)) | |
106 return false; | |
107 } | |
108 } else { | |
109 // Map doesn't exist, create it now if needed. | |
110 if (!changes.empty()) { | |
111 if (!CreateNewMap(namespace_key, &batch, &map_id)) | |
112 return false; | |
113 } | |
114 } | |
115 | |
116 // WriteValuesToMap won't do anything if changes is empty. | |
117 WriteValuesToMap(map_id, changes, &batch); | |
118 | |
119 s = db_->Write(leveldb::WriteOptions(), &batch); | |
120 return DatabaseErrorCheck(s.ok()); | |
121 } | |
122 | |
123 bool SessionStorageDatabase::ShallowCopyNamespace(int64 namespace_id, | |
124 int64 new_namespace_id) { | |
michaeln
2012/04/25 08:32:12
pseudo code noodling...
WriteBatch batch;
// set
marja
2012/04/25 15:44:27
Done.
| |
125 // Go through all origins in the namespace |namespace_id|, create placeholders | |
126 // for them in |new_namespace_id|, and associate them with the existing maps. | |
127 | |
128 // Example, data before shallow copy: | |
129 // | map-1 | 1 (refcount) | | |
130 // | map-1-a | b | | |
131 // | namespace-1 (1 = namespace id) | dummy | | |
132 // | namespace-1-origin1 | 1 (mapid) | | |
133 | |
134 // Example, data after shallow copy: | |
135 // | map-1 | 2 (inc. refcount) | | |
136 // | map-1-a | b | | |
137 // | namespace-1 (1 = namespace id) | dummy | | |
138 // | namespace-1-origin1 | 1 (mapid) | | |
139 // | namespace-2 | dummy | | |
140 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
141 | |
142 if (!LazyOpen(true)) | |
143 return false; | |
144 | |
145 leveldb::WriteBatch batch; | |
146 batch.Put(NamespaceStartKey(new_namespace_id, namespace_offset_), ""); | |
147 | |
148 // Ensure that the next namespace id is up to date. | |
149 if (!UpdateNextNamespaceId(new_namespace_id, &batch)) | |
150 return false; | |
151 | |
152 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
153 std::string namespace_start_key = | |
154 NamespaceStartKey(namespace_id, namespace_offset_); | |
155 it->Seek(namespace_start_key); | |
156 if (it->status().IsNotFound()) { | |
157 // It's possible that the namespace doesn't contain any data. (This happens | |
158 // when the namespace doesn't contain any areas.) We don't need to do | |
159 // anything. | |
160 return true; | |
161 } | |
162 if (!DatabaseErrorCheck(it->status().ok())) | |
163 return false; | |
164 | |
165 // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins. | |
166 for (it->Next(); it->Valid(); it->Next()) { | |
167 std::string key = it->key().ToString(); | |
168 if (key.find(namespace_start_key) != 0) { | |
169 // Iterated past the origins for this namespace. | |
170 break; | |
171 } | |
172 std::string map_id = it->value().ToString(); | |
173 // Increase the ref count for the map. | |
174 std::string map_key = MapRefCountKey(map_id); | |
175 int64 old_ref_count = 0; | |
176 if (!GetRefCount(map_key, &old_ref_count)) | |
177 return false; | |
178 batch.Put(map_key, base::Int64ToString(++old_ref_count)); | |
179 // Associate the map with the new namespace. | |
180 size_t second_dash = key.find('-', namespace_start_key.length()); | |
181 std::string origin = key.substr(second_dash + 1); | |
182 std::string new_namespace_key = NamespaceKey( | |
183 base::Int64ToString(new_namespace_id + namespace_offset_), origin); | |
184 batch.Put(new_namespace_key, map_id); | |
185 } | |
186 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
187 return DatabaseErrorCheck(s.ok()); | |
188 } | |
189 | |
190 bool SessionStorageDatabase::DeepCopy(int64 namespace_id, const GURL& origin) { | |
michaeln
2012/04/25 08:32:12
pseudo code noodling...
if (!GetMapForArea(namesp
marja
2012/04/25 15:44:27
Done.
| |
191 // Example, data before deep copy: | |
192 // | namespace-1 (1 = namespace id) | dummy | | |
193 // | namespace-1-origin1 | 1 (mapid) | | |
194 // | namespace-2 | dummy | | |
195 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
196 // | map-1 | 2 (refcount) | | |
197 // | map-1-a | b | | |
198 | |
199 // Example, data after deep copy copy: | |
200 // | namespace-1 (1 = namespace id) | dummy | | |
201 // | namespace-1-origin1 | 1 (mapid) | | |
202 // | namespace-2 | dummy | | |
203 // | namespace-2-origin1 | 2 (mapid) << references the new map | |
204 // | map-1 | 1 (dec. refcount) | | |
205 // | map-1-a | b | | |
206 // | map-2 | 1 (refcount) | | |
207 // | map-2-a | b | | |
208 | |
209 if (!LazyOpen(true)) | |
210 return false; | |
211 | |
212 leveldb::Status s; | |
213 std::string namespace_key = | |
214 NamespaceKey(namespace_id, namespace_offset_, origin); | |
215 leveldb::WriteBatch batch; | |
216 std::string old_map_id; | |
217 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); | |
218 if (!ConsistencyCheck(!s.IsNotFound())) | |
219 return false; | |
220 if (!DatabaseErrorCheck(s.ok())) | |
221 return false; | |
222 | |
223 // If this copy is the the only "shallow" copy of the map, no deep copying is | |
michaeln
2012/04/25 08:32:12
Is there any case where the higher level code woul
marja
2012/04/25 15:44:27
Atm this can happen if we do a shallow copy, write
| |
224 // needed. | |
225 int64 ref_count = 0; | |
226 if (!GetRefCount(MapRefCountKey(old_map_id), &ref_count)) | |
227 return false; | |
228 if (ref_count == 1) | |
229 return true; | |
230 | |
231 std::string new_map_id; | |
232 if (!CreateNewMap(namespace_key, &batch, &new_map_id)) | |
233 return false; | |
234 | |
235 // Copy the values in the map. | |
236 ValuesMap values; | |
237 if (!ReadMap(old_map_id, &values, false)) | |
238 return false; | |
239 WriteValuesToMap(new_map_id, values, &batch); | |
240 | |
241 if (!DecreaseRefCount(old_map_id, 1, &batch)) | |
242 return false; | |
243 | |
244 s = db_->Write(leveldb::WriteOptions(), &batch); | |
245 return DatabaseErrorCheck(s.ok()); | |
246 } | |
247 | |
248 bool SessionStorageDatabase::DisassociateMap(int64 namespace_id, | |
249 const GURL& origin) { | |
michaeln
2012/04/25 08:32:12
ResetArea(id, origin)
pseudo code noodling...
in
marja
2012/04/25 15:44:27
This turned out to be the same than DeleteOrigin -
| |
250 if (!LazyOpen(true)) | |
251 return false; | |
252 | |
253 leveldb::Status s; | |
254 std::string namespace_key = | |
255 NamespaceKey(namespace_id, namespace_offset_, origin); | |
256 std::string old_map_id; | |
257 s = db_->Get(leveldb::ReadOptions(), namespace_key, &old_map_id); | |
258 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
259 return false; | |
260 if (s.IsNotFound()) | |
261 return true; | |
262 | |
263 leveldb::WriteBatch batch; | |
264 if (!DecreaseRefCount(old_map_id, 1, &batch)) | |
265 return false; | |
266 batch.Delete(namespace_key); | |
267 s = db_->Write(leveldb::WriteOptions(), &batch); | |
268 return DatabaseErrorCheck(s.ok()); | |
269 } | |
270 | |
271 bool SessionStorageDatabase::DeleteOrigin(int64 namespace_id, | |
272 const GURL& origin) { | |
273 if (!LazyOpen(false)) { | |
274 // No need to create the database if it doesn't exist. | |
275 return true; | |
276 } | |
277 leveldb::WriteBatch batch; | |
278 if (!DeleteOrigin(base::Int64ToString(namespace_id), origin.spec(), &batch)) | |
279 return false; | |
280 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
281 return DatabaseErrorCheck(s.ok()); | |
282 } | |
283 | |
284 bool SessionStorageDatabase::DeleteNamespace(int64 namespace_id) { | |
michaeln
2012/04/25 08:32:12
pseudo code noodling...
std::vector<std::pair<GUR
marja
2012/04/25 15:44:27
Done.
| |
285 if (!LazyOpen(false)) { | |
286 // No need to create the database if it doesn't exist. | |
287 return true; | |
288 } | |
289 std::string namespace_id_str = base::Int64ToString(namespace_id); | |
290 std::string namespace_start_key = NamespaceStartKey(namespace_id_str); | |
291 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
292 it->Seek(namespace_start_key); | |
293 if (it->status().IsNotFound()) | |
294 return true; | |
295 if (!DatabaseErrorCheck(it->status().ok())) | |
296 return false; | |
297 leveldb::WriteBatch batch; | |
298 // Skip the dummy entry "namespace-<namespaceid>"; then iterate the origins in | |
299 // the namespace. | |
300 for (it->Next(); it->Valid(); it->Next()) { | |
301 // Key is of the form "namespace-<namespaceid>-<origin>". | |
302 std::string key = it->key().ToString(); | |
303 std::string namespace_prefix = NamespacePrefix(); | |
304 if (key.find(namespace_prefix) != 0) { | |
305 // Iterated beyond the keys for this namespace. | |
306 break; | |
307 } | |
308 int prefix_length = namespace_prefix.length(); | |
309 size_t second_dash = key.find('-', prefix_length); | |
310 if (second_dash == std::string::npos || | |
311 key.substr(prefix_length, | |
312 second_dash - prefix_length) != namespace_id_str) { | |
313 // Iterated beyond the keys for this namespace. | |
314 break; | |
315 } | |
316 std::string origin = key.substr(second_dash + 1); | |
317 if (!DeleteOrigin(namespace_id_str, origin, &batch)) | |
318 return false; | |
319 } | |
320 batch.Delete(namespace_start_key); | |
321 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
322 return DatabaseErrorCheck(s.ok()); | |
323 } | |
324 | |
325 bool SessionStorageDatabase::DeleteLeftoverData() { | |
326 if (!LazyOpen(false)) { | |
327 // No need to create the database if it doesn't exist. | |
328 return true; | |
329 } | |
330 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
331 std::string namespace_prefix = NamespacePrefix(); | |
332 it->Seek(namespace_prefix); | |
333 if (it->status().IsNotFound()) { | |
334 // No data to delete. | |
335 return true; | |
336 } | |
337 if (!DatabaseErrorCheck(it->status().ok())) | |
338 return false; | |
339 | |
340 // Find out how much we need to decrease the ref counts for each map referred | |
341 // to by one of the namespaces. Multiple namespaces can refer to the same map. | |
342 std::map<std::string, int> referred_maps; | |
343 leveldb::WriteBatch batch; | |
344 | |
345 for (it->Next(); it->Valid(); it->Next()) { | |
346 std::string key = it->key().ToString(); | |
347 if (key.find(namespace_prefix) != 0) { | |
348 // Iterated beyond the "namespace-" keys. | |
349 break; | |
350 } | |
351 size_t second_dash = key.find('-', namespace_prefix.length()); | |
352 if (second_dash != std::string::npos) { | |
353 // The key is of the form "namespace-<namespaceid>-<origin>", and the | |
354 // value is a map id. Decrease the ref count for that map. | |
355 ++referred_maps[it->value().ToString()]; | |
356 } | |
357 batch.Delete(key); | |
358 } | |
359 // Decrease the ref counts of the maps. | |
360 for (std::map<std::string, int>::const_iterator it = referred_maps.begin(); | |
361 it != referred_maps.end(); ++it) { | |
362 if (!DecreaseRefCount(it->first, it->second, &batch)) | |
363 return false; | |
364 } | |
365 | |
366 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
michaeln
2012/04/25 08:32:12
I think this results in an empty session storage d
marja
2012/04/25 15:44:27
Deleted it for now.
| |
367 return DatabaseErrorCheck(s.ok()); | |
368 } | |
369 | |
370 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | |
371 if (db_error_ || is_inconsistent_) { | |
372 // Don't try to open a database that we know has failed already. | |
373 return false; | |
374 } | |
375 if (IsOpen()) | |
376 return true; | |
377 | |
378 if (!create_if_needed && | |
379 (!file_util::PathExists(file_path_) || | |
380 file_util::IsDirectoryEmpty(file_path_))) { | |
381 // If the directory doesn't exist already and we haven't been asked to | |
382 // create a file on disk, then we don't bother opening the database. This | |
383 // means we wait until we absolutely need to put something onto disk before | |
384 // we do so. | |
385 return false; | |
386 } | |
387 | |
388 leveldb::DB* db; | |
389 leveldb::Status s = TryToOpen(file_path_, &db); | |
390 if (!s.ok()) { | |
391 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
392 << ", error: " << s.ToString(); | |
393 DCHECK(db == NULL); | |
394 | |
395 // Clear the directory and try again. | |
396 file_util::Delete(file_path_, true); | |
397 s = TryToOpen(file_path_, &db); | |
398 if (!s.ok()) { | |
399 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
400 << ", error: " << s.ToString(); | |
401 DCHECK(db == NULL); | |
402 db_error_ = true; | |
403 return false; | |
404 } | |
405 } | |
406 db_.reset(db); | |
407 | |
408 return GetNextNamespaceId(&namespace_offset_); | |
409 } | |
410 | |
411 leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path, | |
412 leveldb::DB** db) { | |
413 leveldb::Options options; | |
414 // The directory exists but a valid leveldb database might not exist inside it | |
415 // (e.g., a subset of the needed files might be missing). Handle this | |
416 // situation gracefully by creating the database now. | |
417 options.create_if_missing = true; | |
418 #if defined(OS_WIN) | |
419 return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db); | |
420 #elif defined(OS_POSIX) | |
421 return leveldb::DB::Open(options, file_path.value(), db); | |
422 #endif | |
423 } | |
424 | |
425 bool SessionStorageDatabase::IsOpen() const { | |
426 return db_.get() != NULL; | |
427 } | |
428 | |
429 bool SessionStorageDatabase::ConsistencyCheck(bool ok) { | |
430 DCHECK(ok); | |
431 if (!ok) { | |
432 // We cannot recover the database during this run, e.g., the upper layer can | |
433 // have a different understanding of the database state (shallow and deep | |
434 // copies). Don't do any database operations during this run, and delete the | |
435 // data. During next run, the database is recreated. | |
436 is_inconsistent_ = true; | |
437 file_util::Delete(file_path_, true); | |
438 } | |
439 return ok; | |
440 } | |
441 | |
442 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) { | |
443 if (!ok) { | |
444 // We cannot recover the database during this run, e.g., the upper layer can | |
445 // have a different understanding of the database state (shallow and deep | |
446 // copies). Don't do any database operations during this run, and delete the | |
447 // data. During the next run, the database is recreated. | |
448 db_error_ = true; | |
449 file_util::Delete(file_path_, true); | |
michaeln
2012/04/25 08:32:12
This doesn't look like a viable error handling str
marja
2012/04/25 15:44:27
Ahh, true, we should at least close the db first.
| |
450 } | |
451 return ok; | |
452 } | |
453 | |
454 bool SessionStorageDatabase::ReadMap(const std::string& map_id, | |
455 ValuesMap* result, | |
456 bool only_keys) { | |
457 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
458 std::string map_start_key = MapRefCountKey(map_id); | |
459 it->Seek(map_start_key); | |
460 // The map needs to exist, otherwise we have a stale map_id in the database. | |
461 if (!ConsistencyCheck(!it->status().IsNotFound())) | |
462 return false; | |
463 if (!DatabaseErrorCheck(it->status().ok())) | |
464 return false; | |
465 // Skip the dummy entry "map-<mapid>". | |
466 for (it->Next(); it->Valid(); it->Next()) { | |
467 // Key is of the form "map-<mapid>-<key>". | |
468 std::string key = it->key().ToString(); | |
469 int prefix_length = MapPrefix().length(); | |
470 size_t second_dash = key.find('-', prefix_length); | |
471 if (second_dash == std::string::npos || | |
472 key.substr(prefix_length, second_dash - prefix_length) != map_id) { | |
473 // Iterated beyond the keys in this map. | |
474 break; | |
475 } | |
476 string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1)); | |
477 if (only_keys) { | |
478 (*result)[key16] = NullableString16(true); | |
479 } else { | |
480 // Convert the raw data stored in std::string (it->value()) to raw data | |
481 // stored in string16. | |
482 string16 value; | |
483 size_t len = it->value().size() / sizeof(char16); | |
484 value.resize(len); | |
485 value.assign(reinterpret_cast<const char16*>(it->value().data()), len); | |
486 (*result)[key16] = NullableString16(value, false); | |
michaeln
2012/04/25 08:32:12
is the call to resize needed? i'm not sure if this
marja
2012/04/25 15:44:27
Done.
| |
487 } | |
488 } | |
489 return true; | |
490 } | |
491 | |
492 bool SessionStorageDatabase::CreateNewMap(const std::string& namespace_key, | |
493 leveldb::WriteBatch* batch, | |
494 std::string* map_id) { | |
495 // Create a new map. | |
496 std::string next_map_id_key = NextMapIdKey(); | |
497 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id); | |
498 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
499 return false; | |
500 int64 next_map_id = 0; | |
501 if (s.IsNotFound()) { | |
502 *map_id = "0"; | |
503 } else { | |
504 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id); | |
505 if (!ConsistencyCheck(conversion_ok)) | |
506 return false; | |
507 } | |
508 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); | |
509 batch->Put(namespace_key, *map_id); | |
510 batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1)); | |
511 return true; | |
512 } | |
513 | |
514 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id, | |
515 const ValuesMap& values, | |
516 leveldb::WriteBatch* batch) { | |
517 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); | |
518 ++it) { | |
519 NullableString16 value = it->second; | |
520 std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); | |
521 if (value.is_null()) { | |
522 batch->Delete(key); | |
michaeln
2012/04/25 08:32:12
can batch->Delete or batch->Put fail... ah... i se
| |
523 } else { | |
524 // Convert the raw data stored in string16 to raw data stored in | |
525 // std::string. | |
526 const char* data = reinterpret_cast<const char*>(value.string().data()); | |
527 size_t size = value.string().size() * 2; | |
528 batch->Put(key, std::string(data, size)); | |
michaeln
2012/04/25 08:32:12
i think you could use the Slice(const char* d, siz
marja
2012/04/25 15:44:27
Done.
| |
529 } | |
530 } | |
531 } | |
532 | |
533 bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) { | |
534 std::string next_namespace_id_string; | |
535 leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(), | |
536 &next_namespace_id_string); | |
537 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
538 return false; | |
539 if (s.IsNotFound()) { | |
540 *next_namespace_id = 0; | |
541 return true; | |
542 } | |
543 bool conversion_ok = | |
544 base::StringToInt64(next_namespace_id_string, next_namespace_id); | |
545 return ConsistencyCheck(conversion_ok); | |
546 } | |
547 | |
548 bool SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id, | |
549 leveldb::WriteBatch* batch) { | |
550 int64 next_namespace_id; | |
551 if (!GetNextNamespaceId(&next_namespace_id)) | |
552 return false; | |
553 if (next_namespace_id < namespace_id + namespace_offset_ + 1) { | |
554 next_namespace_id = namespace_id + namespace_offset_ + 1; | |
555 batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id)); | |
556 } | |
557 return true; | |
558 } | |
559 | |
560 bool SessionStorageDatabase::GetRefCount(const std::string& map_key, | |
561 int64* ref_count) { | |
562 std::string ref_count_string; | |
563 leveldb::Status s = | |
564 db_->Get(leveldb::ReadOptions(), map_key, &ref_count_string); | |
565 if (!ConsistencyCheck(s.ok())) | |
566 return false; | |
567 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count); | |
568 return ConsistencyCheck(conversion_ok); | |
569 } | |
570 | |
571 bool SessionStorageDatabase::DecreaseRefCount(const std::string& map_id, | |
572 int decrease, | |
573 leveldb::WriteBatch* batch) { | |
574 // Decrease the ref count for the map. | |
575 std::string map_key = MapRefCountKey(map_id); | |
576 int64 ref_count = 0; | |
577 if (!GetRefCount(map_key, &ref_count)) | |
578 return false; | |
579 if (!ConsistencyCheck(decrease <= ref_count)) | |
580 return false; | |
581 ref_count -= decrease; | |
582 if (ref_count > 0) { | |
583 batch->Put(map_key, base::Int64ToString(ref_count)); | |
584 } else { | |
585 // Clear all keys in the map. | |
586 if (!ClearMap(map_id, batch)) | |
587 return false; | |
588 batch->Delete(map_key); | |
589 } | |
590 return true; | |
591 } | |
592 | |
593 bool SessionStorageDatabase::ClearMap(const std::string& map_id, | |
594 leveldb::WriteBatch* batch) { | |
595 ValuesMap values; | |
596 if (!ReadMap(map_id, &values, true)) | |
597 return false; | |
598 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) | |
599 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); | |
600 return true; | |
601 } | |
602 | |
603 bool SessionStorageDatabase::DeleteOrigin(const std::string& namespace_id, | |
604 const std::string& origin, | |
605 leveldb::WriteBatch* batch) { | |
606 std::string namespace_key = NamespaceKey(namespace_id, origin); | |
607 std::string map_id; | |
608 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, &map_id); | |
609 if (s.IsNotFound()) | |
610 return true; // Nothing to delete. | |
611 if (!DatabaseErrorCheck(s.ok())) | |
612 return false; | |
613 if (!DecreaseRefCount(map_id, 1, batch)) | |
614 return false; | |
615 batch->Delete(namespace_key); | |
616 return true; | |
617 } | |
618 | |
619 std::string SessionStorageDatabase::NamespaceStartKey( | |
620 const std::string& namespace_id) { | |
621 return base::StringPrintf("namespace-%s", namespace_id.c_str()); | |
622 } | |
623 | |
624 std::string SessionStorageDatabase::NamespaceStartKey(int64 namespace_id, | |
625 int64 namespace_offset) { | |
626 return NamespaceStartKey( | |
627 base::Int64ToString(namespace_id + namespace_offset)); | |
628 } | |
629 | |
630 std::string SessionStorageDatabase::NamespaceKey( | |
631 const std::string& namespace_id, const std::string& origin) { | |
632 return base::StringPrintf("namespace-%s-%s", namespace_id.c_str(), | |
633 origin.c_str()); | |
634 } | |
635 | |
636 std::string SessionStorageDatabase::NamespaceKey( | |
637 int64 namespace_id, int64 namespace_offset, const GURL& origin) { | |
638 return NamespaceKey(base::Int64ToString(namespace_id + namespace_offset), | |
639 origin.spec()); | |
640 } | |
641 | |
642 std::string SessionStorageDatabase::NamespacePrefix() { | |
643 return std::string("namespace-"); | |
644 } | |
645 | |
646 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) { | |
647 return base::StringPrintf("map-%s", map_id.c_str()); | |
648 } | |
649 | |
650 std::string SessionStorageDatabase::MapKey(const std::string& map_id, | |
651 const std::string& key) { | |
652 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); | |
653 } | |
654 | |
655 std::string SessionStorageDatabase::MapPrefix() { | |
656 return std::string("map-"); | |
657 } | |
658 | |
659 std::string SessionStorageDatabase::NextNamespaceIdKey() { | |
660 return "next-namespace-id"; | |
661 } | |
662 | |
663 std::string SessionStorageDatabase::NextMapIdKey() { | |
664 return "next-map-id"; | |
665 } | |
666 | |
667 } // namespace dom_storage | |
OLD | NEW |