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) | |
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::ReadAreaValues(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 std::string map_id; | |
57 bool exists; | |
58 if (!GetMapForArea(namespace_id, origin, &exists, &map_id)) | |
59 return; | |
60 if (exists) | |
61 ReadMap(map_id, result, false); | |
62 } | |
63 | |
64 bool SessionStorageDatabase::CommitAreaChanges(int64 namespace_id, | |
65 const GURL& origin, | |
66 bool clear_all_first, | |
67 const ValuesMap& changes) { | |
68 // Even if |changes| is empty, we need to write the appropriate placeholders | |
69 // in the database, so that it can be later shallow-copied succssfully. | |
70 if (!LazyOpen(true)) | |
71 return false; | |
72 | |
73 leveldb::WriteBatch batch; | |
74 // Ensure that the keys "namespace-" "namespace-N" (see the schema above) | |
75 // exist. | |
76 if (!CreateNamespace(namespace_id, true, &batch)) | |
michaeln
2012/04/26 05:07:43
webkit style rubbing off on me too much maybe... m
marja
2012/04/27 07:34:47
Done.
| |
77 return false; | |
78 | |
79 std::string map_id; | |
80 bool exists; | |
81 if (!GetMapForArea(namespace_id, origin, &exists, &map_id)) | |
82 return false; | |
83 if (exists) { | |
84 // We shouldn't write data to a shallow copy. | |
85 int64 ref_count; | |
86 if (!GetMapRefCount(map_id, &ref_count)) | |
87 return false; | |
88 if (!ConsistencyCheck(ref_count == 1)) | |
michaeln
2012/04/26 05:07:43
This feel like a programming error more than a dat
marja
2012/04/27 07:34:47
Done, added CallerErrorCheck which will be called
| |
89 return false; | |
90 if (clear_all_first) { | |
91 if (!ClearMap(map_id, &batch)) | |
92 return false; | |
93 } | |
94 } else { | |
95 // Map doesn't exist, create it now if needed. | |
96 if (!changes.empty()) { | |
97 if (!CreateMapForArea(namespace_id, origin, &map_id, &batch)) | |
98 return false; | |
99 } | |
100 } | |
101 | |
102 WriteValuesToMap(map_id, changes, &batch); | |
103 | |
104 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
105 return DatabaseErrorCheck(s.ok()); | |
106 } | |
107 | |
108 bool SessionStorageDatabase::CloneNamespace(int64 namespace_id, | |
109 int64 new_namespace_id) { | |
110 // Go through all origins in the namespace |namespace_id|, create placeholders | |
111 // for them in |new_namespace_id|, and associate them with the existing maps. | |
112 | |
113 // Example, data before shallow copy: | |
114 // | map-1 | 1 (refcount) | | |
115 // | map-1-a | b | | |
116 // | namespace-1 (1 = namespace id) | dummy | | |
117 // | namespace-1-origin1 | 1 (mapid) | | |
118 | |
119 // Example, data after shallow copy: | |
120 // | map-1 | 2 (inc. refcount) | | |
121 // | map-1-a | b | | |
122 // | namespace-1 (1 = namespace id) | dummy | | |
123 // | namespace-1-origin1 | 1 (mapid) | | |
124 // | namespace-2 | dummy | | |
125 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
126 | |
127 if (!LazyOpen(true)) | |
128 return false; | |
129 | |
130 leveldb::WriteBatch batch; | |
131 if (!CreateNamespace(new_namespace_id, false, &batch)) | |
michaeln
2012/04/26 05:07:43
simlarly, const bool kFailIfExists = false local f
marja
2012/04/27 07:34:47
Done.
| |
132 return false; | |
133 | |
134 std::map<std::string, std::string> areas; | |
135 if (!GetAreasInNamespace(namespace_id, &areas)) | |
136 return false; | |
137 | |
138 for (std::map<std::string, std::string>::const_iterator it = areas.begin(); | |
139 it != areas.end(); ++it) { | |
140 const std::string& origin = it->first; | |
141 const std::string& map_id = it->second; | |
142 if (!IncreaseMapRefCount(map_id, &batch)) | |
143 return false; | |
144 AddAreaToNamespace(new_namespace_id, origin, map_id, &batch); | |
145 } | |
146 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
147 return DatabaseErrorCheck(s.ok()); | |
148 } | |
149 | |
150 bool SessionStorageDatabase::DeepCopyArea(int64 namespace_id, | |
151 const GURL& origin) { | |
152 // Example, data before deep copy: | |
153 // | namespace-1 (1 = namespace id) | dummy | | |
154 // | namespace-1-origin1 | 1 (mapid) | | |
155 // | namespace-2 | dummy | | |
156 // | namespace-2-origin1 | 1 (mapid) << references the same map | |
157 // | map-1 | 2 (refcount) | | |
158 // | map-1-a | b | | |
159 | |
160 // Example, data after deep copy copy: | |
161 // | namespace-1 (1 = namespace id) | dummy | | |
162 // | namespace-1-origin1 | 1 (mapid) | | |
163 // | namespace-2 | dummy | | |
164 // | namespace-2-origin1 | 2 (mapid) << references the new map | |
165 // | map-1 | 1 (dec. refcount) | | |
166 // | map-1-a | b | | |
167 // | map-2 | 1 (refcount) | | |
168 // | map-2-a | b | | |
169 | |
170 if (!LazyOpen(true)) | |
michaeln
2012/04/26 05:07:43
maybe LazyOpen(false) here, if the db doesnt exist
marja
2012/04/27 07:34:47
Below we check that the thing to copy must exist,
| |
171 return false; | |
172 | |
173 std::string old_map_id; | |
174 bool exists; | |
175 if (!GetMapForArea(namespace_id, origin, &exists, &old_map_id)) | |
176 return false; | |
177 if (!ConsistencyCheck(exists)) | |
178 return false; | |
179 | |
180 // At least one other namespace should refer to the same map. Otherwise this | |
181 // copy is not shallow, and the upper layer shouldn't call this function. | |
182 int64 ref_count; | |
183 if (!GetMapRefCount(old_map_id, &ref_count)) | |
184 return false; | |
185 if (!ConsistencyCheck(ref_count > 1)) | |
186 return false; | |
187 | |
188 leveldb::WriteBatch batch; | |
189 std::string new_map_id; | |
190 if (!CreateMapForArea(namespace_id, origin, &new_map_id, &batch)) | |
191 return false; | |
192 | |
193 // Copy the values in the map. | |
194 ValuesMap values; | |
195 if (!ReadMap(old_map_id, &values, false)) | |
196 return false; | |
197 WriteValuesToMap(new_map_id, values, &batch); | |
198 | |
199 if (!DecreaseMapRefCount(old_map_id, 1, &batch)) | |
200 return false; | |
201 | |
202 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
203 return DatabaseErrorCheck(s.ok()); | |
204 } | |
205 | |
206 bool SessionStorageDatabase::DeleteArea(int64 namespace_id, | |
207 const GURL& origin) { | |
208 if (!LazyOpen(false)) { | |
209 // No need to create the database if it doesn't exist. | |
210 return true; | |
211 } | |
212 leveldb::WriteBatch batch; | |
213 if (!DeleteArea(namespace_id, origin.spec(), &batch)) | |
214 return false; | |
215 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
216 return DatabaseErrorCheck(s.ok()); | |
217 } | |
218 | |
219 bool SessionStorageDatabase::DeleteNamespace(int64 namespace_id) { | |
220 if (!LazyOpen(false)) { | |
221 // No need to create the database if it doesn't exist. | |
222 return true; | |
223 } | |
224 // Itereate through the areas in the namespace. | |
225 leveldb::WriteBatch batch; | |
226 std::map<std::string, std::string> areas; | |
227 if (!GetAreasInNamespace(namespace_id, &areas)) | |
228 return false; | |
229 for (std::map<std::string, std::string>::const_iterator it = areas.begin(); | |
230 it != areas.end(); ++it) { | |
231 const std::string& origin = it->first; | |
michaeln
2012/04/26 05:07:43
thnx for the self-document-code that tells me what
| |
232 if (!DeleteArea(namespace_id, origin, &batch)) | |
233 return false; | |
234 } | |
235 batch.Delete(NamespaceStartKey(namespace_id, namespace_offset_)); | |
236 leveldb::Status s = db_->Write(leveldb::WriteOptions(), &batch); | |
237 return DatabaseErrorCheck(s.ok()); | |
238 } | |
239 | |
240 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | |
michaeln
2012/04/26 05:07:43
I think we may need a mutex locker around this met
marja
2012/04/27 07:34:47
Done.
| |
241 if (db_error_ || is_inconsistent_) { | |
michaeln
2012/04/26 05:07:43
If we trip on any 'consistency' error that's reall
marja
2012/04/27 07:34:47
Now this should distinguish better between incorre
| |
242 // Don't try to open a database that we know has failed already. | |
243 return false; | |
244 } | |
245 if (IsOpen()) | |
246 return true; | |
247 | |
248 if (!create_if_needed && | |
249 (!file_util::PathExists(file_path_) || | |
250 file_util::IsDirectoryEmpty(file_path_))) { | |
251 // If the directory doesn't exist already and we haven't been asked to | |
252 // create a file on disk, then we don't bother opening the database. This | |
253 // means we wait until we absolutely need to put something onto disk before | |
254 // we do so. | |
255 return false; | |
256 } | |
257 | |
258 leveldb::DB* db; | |
259 leveldb::Status s = TryToOpen(file_path_, &db); | |
260 if (!s.ok()) { | |
261 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
262 << ", error: " << s.ToString(); | |
263 DCHECK(db == NULL); | |
264 | |
265 // Clear the directory and try again. | |
266 file_util::Delete(file_path_, true); | |
267 s = TryToOpen(file_path_, &db); | |
268 if (!s.ok()) { | |
269 LOG(WARNING) << "Failed to open leveldb in " << file_path_.value() | |
270 << ", error: " << s.ToString(); | |
271 DCHECK(db == NULL); | |
272 db_error_ = true; | |
273 return false; | |
274 } | |
275 } | |
276 db_.reset(db); | |
277 | |
278 return GetNextNamespaceId(&namespace_offset_); | |
279 } | |
280 | |
281 leveldb::Status SessionStorageDatabase::TryToOpen(const FilePath& file_path, | |
282 leveldb::DB** db) { | |
283 leveldb::Options options; | |
284 // The directory exists but a valid leveldb database might not exist inside it | |
285 // (e.g., a subset of the needed files might be missing). Handle this | |
286 // situation gracefully by creating the database now. | |
287 options.create_if_missing = true; | |
288 #if defined(OS_WIN) | |
289 return leveldb::DB::Open(options, WideToUTF8(file_path.value()), db); | |
290 #elif defined(OS_POSIX) | |
291 return leveldb::DB::Open(options, file_path.value(), db); | |
292 #endif | |
293 } | |
294 | |
295 bool SessionStorageDatabase::IsOpen() const { | |
296 return db_.get() != NULL; | |
297 } | |
298 | |
299 bool SessionStorageDatabase::ConsistencyCheck(bool ok) { | |
300 DCHECK(ok); | |
301 is_inconsistent_ = !ok; | |
302 // We cannot recover the database during this run, e.g., the upper layer can | |
303 // have a different understanding of the database state (shallow and deep | |
304 // copies). | |
305 // TODO(marja): Error handling. | |
306 return ok; | |
307 } | |
308 | |
309 bool SessionStorageDatabase::DatabaseErrorCheck(bool ok) { | |
310 db_error_ = !ok; | |
311 // TODO(marja): Error handling. | |
312 return ok; | |
313 } | |
314 | |
315 bool SessionStorageDatabase::CreateNamespace(int64 namespace_id, | |
316 bool ok_if_exists, | |
317 leveldb::WriteBatch* batch) { | |
318 std::string namespace_prefix = NamespacePrefix(); | |
319 std::string dummy; | |
320 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_prefix, | |
321 &dummy); | |
322 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
323 return false; | |
324 if (s.IsNotFound()) | |
325 batch->Put(namespace_prefix, ""); | |
326 | |
327 std::string namespace_start_key = | |
328 NamespaceStartKey(namespace_id, namespace_offset_); | |
329 s = db_->Get(leveldb::ReadOptions(), namespace_start_key, &dummy); | |
330 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
331 return false; | |
332 if (s.IsNotFound()) { | |
333 batch->Put(namespace_start_key, ""); | |
334 return UpdateNextNamespaceId(namespace_id, batch); | |
335 } | |
336 return ConsistencyCheck(ok_if_exists); | |
michaeln
2012/04/26 05:07:43
Is this really a consistency error? Or possibly a
marja
2012/04/27 07:34:47
Done.
| |
337 } | |
338 | |
339 bool SessionStorageDatabase::GetNextNamespaceId(int64* next_namespace_id) { | |
340 std::string next_namespace_id_string; | |
341 leveldb::Status s = db_->Get(leveldb::ReadOptions(), NextNamespaceIdKey(), | |
342 &next_namespace_id_string); | |
343 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
344 return false; | |
345 if (s.IsNotFound()) { | |
346 *next_namespace_id = 0; | |
347 return true; | |
348 } | |
349 bool conversion_ok = | |
350 base::StringToInt64(next_namespace_id_string, next_namespace_id); | |
351 return ConsistencyCheck(conversion_ok); | |
352 } | |
353 | |
354 bool SessionStorageDatabase::UpdateNextNamespaceId(int64 namespace_id, | |
355 leveldb::WriteBatch* batch) { | |
356 int64 next_namespace_id; | |
357 if (!GetNextNamespaceId(&next_namespace_id)) | |
358 return false; | |
359 if (next_namespace_id < namespace_id + namespace_offset_ + 1) { | |
360 next_namespace_id = namespace_id + namespace_offset_ + 1; | |
361 batch->Put(NextNamespaceIdKey(), base::Int64ToString(next_namespace_id)); | |
362 } | |
363 return true; | |
364 } | |
365 | |
366 bool SessionStorageDatabase::GetAreasInNamespace( | |
367 int64 namespace_id, | |
368 std::map<std::string, std::string>* areas) { | |
369 return GetAreasInNamespace(NamespaceIdStr(namespace_id, namespace_offset_), | |
370 areas); | |
371 } | |
372 | |
373 bool SessionStorageDatabase::GetAreasInNamespace( | |
374 const std::string& namespace_id_str, | |
375 std::map<std::string, std::string>* areas) { | |
376 std::string namespace_start_key = NamespaceStartKey(namespace_id_str); | |
377 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
378 it->Seek(namespace_start_key); | |
379 if (it->status().IsNotFound()) { | |
380 // The namespace_start_key is not found when the namespace doesn't contain | |
381 // any areas. We don't need to do anything. | |
382 return true; | |
383 } | |
384 if (!DatabaseErrorCheck(it->status().ok())) | |
385 return false; | |
386 | |
387 // Skip the dummy entry "namespace-<namespaceid>" and iterate the origins. | |
388 for (it->Next(); it->Valid(); it->Next()) { | |
389 std::string key = it->key().ToString(); | |
390 if (key.find(namespace_start_key) != 0) { | |
391 // Iterated past the origins for this namespace. | |
392 break; | |
393 } | |
394 size_t second_dash = key.find('-', namespace_start_key.length()); | |
395 if (!ConsistencyCheck(second_dash != std::string::npos)) | |
396 return false; | |
397 std::string origin = key.substr(second_dash + 1); | |
398 std::string map_id = it->value().ToString(); | |
399 (*areas)[origin] = map_id; | |
400 } | |
401 return true; | |
402 } | |
403 | |
404 void SessionStorageDatabase::AddAreaToNamespace(int64 namespace_id, | |
405 const std::string& origin, | |
406 const std::string& map_id, | |
407 leveldb::WriteBatch* batch) { | |
408 std::string namespace_key = NamespaceKey( | |
409 NamespaceIdStr(namespace_id, namespace_offset_), origin); | |
410 batch->Put(namespace_key, map_id); | |
411 } | |
412 | |
413 bool SessionStorageDatabase::DeleteArea(int64 namespace_id, | |
414 const std::string& origin, | |
415 leveldb::WriteBatch* batch) { | |
416 return DeleteArea(NamespaceIdStr(namespace_id, namespace_offset_), | |
417 origin, batch); | |
418 } | |
419 | |
420 bool SessionStorageDatabase::DeleteArea(const std::string& namespace_id_str, | |
421 const std::string& origin, | |
422 leveldb::WriteBatch* batch) { | |
423 std::string map_id; | |
424 bool exists; | |
425 if (!GetMapForArea(namespace_id_str, origin, &exists, &map_id)) | |
426 return false; | |
427 if (!exists) | |
428 return true; // Nothing to delete. | |
429 if (!DecreaseMapRefCount(map_id, 1, batch)) | |
430 return false; | |
431 std::string namespace_key = NamespaceKey(namespace_id_str, origin); | |
432 batch->Delete(namespace_key); | |
433 return true; | |
434 } | |
435 | |
436 bool SessionStorageDatabase::GetMapForArea(int64 namespace_id, | |
437 const GURL& origin, | |
438 bool* exists, | |
439 std::string* map_id) { | |
440 return GetMapForArea( | |
441 base::Int64ToString(namespace_id + namespace_offset_), | |
442 origin.spec(), exists, map_id); | |
443 } | |
444 | |
445 bool SessionStorageDatabase::GetMapForArea(const std::string& namespace_id_str, | |
446 const std::string& origin, | |
447 bool* exists, std::string* map_id) { | |
448 std::string namespace_key = NamespaceKey(namespace_id_str, origin); | |
449 leveldb::Status s = db_->Get(leveldb::ReadOptions(), namespace_key, map_id); | |
450 if (s.IsNotFound()) { | |
451 *exists = false; | |
452 return true; | |
453 } | |
454 *exists = true; | |
455 return DatabaseErrorCheck(s.ok()); | |
456 } | |
457 | |
458 bool SessionStorageDatabase::CreateMapForArea(int64 namespace_id, | |
459 const GURL& origin, | |
460 std::string* map_id, | |
461 leveldb::WriteBatch* batch) { | |
462 std::string next_map_id_key = NextMapIdKey(); | |
463 leveldb::Status s = db_->Get(leveldb::ReadOptions(), next_map_id_key, map_id); | |
464 if (!DatabaseErrorCheck(s.ok() || s.IsNotFound())) | |
465 return false; | |
466 int64 next_map_id = 0; | |
467 if (s.IsNotFound()) { | |
468 *map_id = "0"; | |
469 } else { | |
470 bool conversion_ok = base::StringToInt64(*map_id, &next_map_id); | |
471 if (!ConsistencyCheck(conversion_ok)) | |
472 return false; | |
473 } | |
474 batch->Put(next_map_id_key, base::Int64ToString(++next_map_id)); | |
475 std::string namespace_key = | |
476 NamespaceKey(namespace_id, namespace_offset_, origin); | |
477 batch->Put(namespace_key, *map_id); | |
478 batch->Put(MapRefCountKey(*map_id), base::Int64ToString(1)); | |
michaeln
2012/04/26 05:07:43
maybe "1"?
marja
2012/04/27 07:34:47
Done.
| |
479 return true; | |
480 } | |
481 | |
482 bool SessionStorageDatabase::ReadMap(const std::string& map_id, | |
483 ValuesMap* result, | |
484 bool only_keys) { | |
485 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
486 std::string map_start_key = MapRefCountKey(map_id); | |
487 it->Seek(map_start_key); | |
488 // The map needs to exist, otherwise we have a stale map_id in the database. | |
489 if (!ConsistencyCheck(!it->status().IsNotFound())) | |
490 return false; | |
491 if (!DatabaseErrorCheck(it->status().ok())) | |
492 return false; | |
493 // Skip the dummy entry "map-<mapid>". | |
494 for (it->Next(); it->Valid(); it->Next()) { | |
495 // Key is of the form "map-<mapid>-<key>". | |
496 std::string key = it->key().ToString(); | |
497 int prefix_length = MapPrefix().length(); | |
498 size_t second_dash = key.find('-', prefix_length); | |
499 if (second_dash == std::string::npos || | |
500 key.substr(prefix_length, second_dash - prefix_length) != map_id) { | |
501 // Iterated beyond the keys in this map. | |
502 break; | |
503 } | |
504 string16 key16 = UTF8ToUTF16(key.substr(second_dash + 1)); | |
505 if (only_keys) { | |
506 (*result)[key16] = NullableString16(true); | |
507 } else { | |
508 // Convert the raw data stored in std::string (it->value()) to raw data | |
509 // stored in string16. | |
510 size_t len = it->value().size() / sizeof(char16); | |
511 const char16* data_ptr = | |
512 reinterpret_cast<const char16*>(it->value().data()); | |
513 (*result)[key16] = NullableString16(string16(data_ptr, len), false); | |
514 } | |
515 } | |
516 return true; | |
517 } | |
518 | |
519 void SessionStorageDatabase::WriteValuesToMap(const std::string& map_id, | |
520 const ValuesMap& values, | |
521 leveldb::WriteBatch* batch) { | |
522 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); | |
523 ++it) { | |
524 NullableString16 value = it->second; | |
525 std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); | |
526 if (value.is_null()) { | |
527 batch->Delete(key); | |
528 } else { | |
529 // Convert the raw data stored in string16 to raw data stored in | |
530 // std::string. | |
531 const char* data = reinterpret_cast<const char*>(value.string().data()); | |
532 size_t size = value.string().size() * 2; | |
533 batch->Put(key, leveldb::Slice(data, size)); | |
534 } | |
535 } | |
536 } | |
537 | |
538 bool SessionStorageDatabase::GetMapRefCount(const std::string& map_id, | |
539 int64* ref_count) { | |
540 std::string ref_count_string; | |
541 leveldb::Status s = db_->Get(leveldb::ReadOptions(), | |
542 MapRefCountKey(map_id), &ref_count_string); | |
543 if (!ConsistencyCheck(s.ok())) | |
544 return false; | |
545 bool conversion_ok = base::StringToInt64(ref_count_string, ref_count); | |
546 return ConsistencyCheck(conversion_ok); | |
547 } | |
548 | |
549 bool SessionStorageDatabase::IncreaseMapRefCount(const std::string& map_id, | |
550 leveldb::WriteBatch* batch) { | |
551 // Increase the ref count for the map. | |
552 int64 old_ref_count; | |
553 if (!GetMapRefCount(map_id, &old_ref_count)) | |
554 return false; | |
555 batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count)); | |
556 return true; | |
557 } | |
558 | |
559 bool SessionStorageDatabase::DecreaseMapRefCount(const std::string& map_id, | |
560 int decrease, | |
561 leveldb::WriteBatch* batch) { | |
562 // Decrease the ref count for the map. | |
563 int64 ref_count; | |
564 if (!GetMapRefCount(map_id, &ref_count)) | |
565 return false; | |
566 if (!ConsistencyCheck(decrease <= ref_count)) | |
567 return false; | |
568 ref_count -= decrease; | |
569 if (ref_count > 0) { | |
570 batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count)); | |
571 } else { | |
572 // Clear all keys in the map. | |
573 if (!ClearMap(map_id, batch)) | |
574 return false; | |
575 batch->Delete(MapRefCountKey(map_id)); | |
576 } | |
577 return true; | |
578 } | |
579 | |
580 bool SessionStorageDatabase::ClearMap(const std::string& map_id, | |
581 leveldb::WriteBatch* batch) { | |
582 ValuesMap values; | |
583 if (!ReadMap(map_id, &values, true)) | |
584 return false; | |
585 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); ++it) | |
586 batch->Delete(MapKey(map_id, UTF16ToUTF8(it->first))); | |
587 return true; | |
588 } | |
589 | |
590 std::string SessionStorageDatabase::NamespaceStartKey( | |
591 const std::string& namespace_id_str) { | |
592 return base::StringPrintf("namespace-%s", namespace_id_str.c_str()); | |
593 } | |
594 | |
595 std::string SessionStorageDatabase::NamespaceStartKey(int64 namespace_id, | |
596 int64 namespace_offset) { | |
597 return NamespaceStartKey(NamespaceIdStr(namespace_id, namespace_offset)); | |
598 } | |
599 | |
600 std::string SessionStorageDatabase::NamespaceKey( | |
601 const std::string& namespace_id_str, const std::string& origin) { | |
602 return base::StringPrintf("namespace-%s-%s", namespace_id_str.c_str(), | |
603 origin.c_str()); | |
604 } | |
605 | |
606 std::string SessionStorageDatabase::NamespaceKey( | |
607 int64 namespace_id, int64 namespace_offset, const GURL& origin) { | |
608 return NamespaceKey(NamespaceIdStr(namespace_id, namespace_offset), | |
609 origin.spec()); | |
610 } | |
611 | |
612 std::string SessionStorageDatabase::NamespaceIdStr(int64 namespace_id, | |
613 int64 namespace_offset) { | |
614 return base::Int64ToString(namespace_id + namespace_offset); | |
615 } | |
616 | |
617 std::string SessionStorageDatabase::NamespacePrefix() { | |
michaeln
2012/04/26 05:07:43
maybe consider making the truely constant 'prefix'
marja
2012/04/27 07:34:47
Done.
| |
618 return std::string("namespace-"); | |
619 } | |
620 | |
621 std::string SessionStorageDatabase::MapRefCountKey(const std::string& map_id) { | |
622 return base::StringPrintf("map-%s", map_id.c_str()); | |
623 } | |
624 | |
625 std::string SessionStorageDatabase::MapKey(const std::string& map_id, | |
626 const std::string& key) { | |
627 return base::StringPrintf("map-%s-%s", map_id.c_str(), key.c_str()); | |
628 } | |
629 | |
630 std::string SessionStorageDatabase::MapPrefix() { | |
631 return std::string("map-"); | |
632 } | |
633 | |
634 std::string SessionStorageDatabase::NextNamespaceIdKey() { | |
635 return "next-namespace-id"; | |
636 } | |
637 | |
638 std::string SessionStorageDatabase::NextMapIdKey() { | |
639 return "next-map-id"; | |
640 } | |
641 | |
642 } // namespace dom_storage | |
OLD | NEW |