blob: 991d1d9e261321e1ddb48a29108cbd4a519bfdd2 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/services/leveldb/leveldb_database_impl.h"
#include <inttypes.h>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include "base/optional.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "components/services/leveldb/public/cpp/util.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace leveldb {
// static
std::unique_ptr<LevelDBDatabaseImpl> LevelDBDatabaseImpl::OpenDirectory(
const leveldb_env::Options& options,
const base::FilePath& directory,
const std::string& dbname,
const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
memory_dump_id,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
StatusCallback callback) {
std::unique_ptr<LevelDBDatabaseImpl> db(new LevelDBDatabaseImpl);
storage::DomStorageDatabase::OpenDirectory(
directory, dbname, options, memory_dump_id,
std::move(blocking_task_runner),
base::BindOnce(&LevelDBDatabaseImpl::OnDatabaseOpened,
db->weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return db;
}
// static
std::unique_ptr<LevelDBDatabaseImpl> LevelDBDatabaseImpl::OpenInMemory(
const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
memory_dump_id,
const std::string& tracking_name,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
StatusCallback callback) {
std::unique_ptr<LevelDBDatabaseImpl> db(new LevelDBDatabaseImpl);
storage::DomStorageDatabase::OpenInMemory(
tracking_name, memory_dump_id, std::move(blocking_task_runner),
base::BindOnce(&LevelDBDatabaseImpl::OnDatabaseOpened,
db->weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return db;
}
LevelDBDatabaseImpl::LevelDBDatabaseImpl() = default;
LevelDBDatabaseImpl::~LevelDBDatabaseImpl() = default;
void LevelDBDatabaseImpl::Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
StatusCallback callback) {
RunDatabaseTask(
base::BindOnce(
[](const std::vector<uint8_t>& key, const std::vector<uint8_t>& value,
const storage::DomStorageDatabase& db) {
return LeveldbStatusToError(db.Put(key, value));
},
key, value),
std::move(callback));
}
void LevelDBDatabaseImpl::Delete(const std::vector<uint8_t>& key,
StatusCallback callback) {
RunDatabaseTask(base::BindOnce(
[](const std::vector<uint8_t>& key,
const storage::DomStorageDatabase& db) {
return LeveldbStatusToError(db.Delete(key));
},
key),
std::move(callback));
}
void LevelDBDatabaseImpl::DeletePrefixed(const std::vector<uint8_t>& key_prefix,
StatusCallback callback) {
RunDatabaseTask(base::BindOnce(
[](const std::vector<uint8_t>& prefix,
const storage::DomStorageDatabase& db) {
WriteBatch batch;
Status status = db.DeletePrefixed(prefix, &batch);
if (!status.ok())
return LeveldbStatusToError(status);
return LeveldbStatusToError(db.Commit(&batch));
},
key_prefix),
std::move(callback));
}
void LevelDBDatabaseImpl::RewriteDB(StatusCallback callback) {
DCHECK(database_);
database_.PostTaskWithThisObject(
FROM_HERE,
base::BindOnce(
[](StatusCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
storage::DomStorageDatabase* db) {
callback_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
LeveldbStatusToError(db->RewriteDB())));
},
std::move(callback), base::SequencedTaskRunnerHandle::Get()));
}
void LevelDBDatabaseImpl::Write(
std::vector<mojom::BatchedOperationPtr> operations,
StatusCallback callback) {
RunDatabaseTask(
base::BindOnce(
[](std::vector<mojom::BatchedOperationPtr> operations,
const storage::DomStorageDatabase& db) {
WriteBatch batch;
for (auto& op : operations) {
switch (op->type) {
case mojom::BatchOperationType::PUT_KEY: {
if (op->value) {
batch.Put(GetSliceFor(op->key), GetSliceFor(*(op->value)));
} else {
batch.Put(GetSliceFor(op->key), Slice());
}
break;
}
case mojom::BatchOperationType::DELETE_KEY: {
batch.Delete(GetSliceFor(op->key));
break;
}
case mojom::BatchOperationType::DELETE_PREFIXED_KEY: {
db.DeletePrefixed(op->key, &batch);
break;
}
case mojom::BatchOperationType::COPY_PREFIXED_KEY: {
// DCHECK is fine here since browser code is the only caller.
DCHECK(op->value);
db.CopyPrefixed(op->key, *(op->value), &batch);
break;
}
}
}
return LeveldbStatusToError(db.Commit(&batch));
},
std::move(operations)),
std::move(callback));
}
void LevelDBDatabaseImpl::Get(const std::vector<uint8_t>& key,
GetCallback callback) {
struct GetResult {
Status status;
storage::DomStorageDatabase::Value value;
};
RunDatabaseTask(base::BindOnce(
[](const std::vector<uint8_t>& key,
const storage::DomStorageDatabase& db) {
GetResult result;
result.status = db.Get(key, &result.value);
return result;
},
key),
base::BindOnce(
[](GetCallback callback, GetResult result) {
std::move(callback).Run(
LeveldbStatusToError(result.status), result.value);
},
std::move(callback)));
}
void LevelDBDatabaseImpl::GetMany(
std::vector<mojom::GetManyRequestPtr> keys_or_prefixes,
GetManyCallback callback) {
RunDatabaseTask(
base::BindOnce(
[](std::vector<mojom::GetManyRequestPtr> keys_or_prefixes,
const storage::DomStorageDatabase& db) {
std::vector<mojom::GetManyResultPtr> data;
for (const auto& request : keys_or_prefixes) {
mojom::GetManyResultPtr result = mojom::GetManyResult::New();
Status status;
if (request->is_key()) {
const std::vector<uint8_t>& key = request->get_key();
storage::DomStorageDatabase::Value value;
status = db.Get(key, &value);
if (status.ok())
result->set_key_value(value);
else
result->set_status(LeveldbStatusToError(status));
} else {
const std::vector<uint8_t>& key_prefix =
request->get_key_prefix();
std::vector<storage::DomStorageDatabase::KeyValuePair> entries;
status = db.GetPrefixed(key_prefix, &entries);
if (status.ok()) {
std::vector<mojom::KeyValuePtr> out_entries;
for (auto& entry : entries) {
out_entries.push_back(
mojom::KeyValue::New(entry.key, entry.value));
}
result->set_key_prefix_values(std::move(out_entries));
} else {
result->set_status(LeveldbStatusToError(status));
}
}
data.push_back(std::move(result));
}
return data;
},
std::move(keys_or_prefixes)),
std::move(callback));
}
void LevelDBDatabaseImpl::GetPrefixed(const std::vector<uint8_t>& key_prefix,
GetPrefixedCallback callback) {
struct GetPrefixedResult {
Status status;
std::vector<storage::DomStorageDatabase::KeyValuePair> entries;
};
RunDatabaseTask(
base::BindOnce(
[](const std::vector<uint8_t>& prefix,
const storage::DomStorageDatabase& db) {
GetPrefixedResult result;
result.status = db.GetPrefixed(prefix, &result.entries);
return result;
},
key_prefix),
base::BindOnce(
[](GetPrefixedCallback callback, GetPrefixedResult result) {
std::vector<mojom::KeyValuePtr> entries;
for (auto& entry : result.entries)
entries.push_back(mojom::KeyValue::New(entry.key, entry.value));
std::move(callback).Run(LeveldbStatusToError(result.status),
std::move(entries));
},
std::move(callback)));
}
void LevelDBDatabaseImpl::CopyPrefixed(
const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix,
StatusCallback callback) {
RunDatabaseTask(base::BindOnce(
[](const std::vector<uint8_t>& prefix,
const std::vector<uint8_t>& new_prefix,
const storage::DomStorageDatabase& db) {
WriteBatch batch;
Status status =
db.CopyPrefixed(prefix, new_prefix, &batch);
if (!status.ok())
return LeveldbStatusToError(status);
return LeveldbStatusToError(db.Commit(&batch));
},
source_key_prefix, destination_key_prefix),
std::move(callback));
}
void LevelDBDatabaseImpl::OnDatabaseOpened(
StatusCallback callback,
base::SequenceBound<storage::DomStorageDatabase> database,
leveldb::Status status) {
database_ = std::move(database);
std::vector<DatabaseTask> tasks;
std::swap(tasks, tasks_to_run_on_open_);
if (status.ok()) {
for (auto& task : tasks)
database_.PostTaskWithThisObject(FROM_HERE, std::move(task));
}
std::move(callback).Run(LeveldbStatusToError(status));
}
} // namespace leveldb