blob: f5d618cc1903cf22b7a89f7c356e473ba422f862 [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/leveldb/leveldb_database_impl.h"
#include <map>
#include <string>
#include "base/rand_util.h"
#include "components/leveldb/env_mojo.h"
#include "components/leveldb/public/cpp/util.h"
#include "mojo/common/common_type_converters.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace leveldb {
namespace {
template <typename T>
uint64_t GetSafeRandomId(const std::map<uint64_t, T>& m) {
// Associate a random unsigned 64 bit handle to |s|, checking for the highly
// improbable id duplication or castability to null.
uint64_t new_id = base::RandUint64();
while ((new_id == 0) || (m.find(new_id) != m.end()))
new_id = base::RandUint64();
return new_id;
}
template <typename FunctionType>
leveldb::Status ForEachWithPrefix(leveldb::DB* db,
const leveldb::Slice& key_prefix,
FunctionType function) {
std::unique_ptr<leveldb::Iterator> it(
db->NewIterator(leveldb::ReadOptions()));
it->Seek(key_prefix);
for (; it->Valid(); it->Next()) {
if (!it->key().starts_with(key_prefix))
break;
function(it->key(), it->value());
}
return it->status();
}
} // namespace
LevelDBDatabaseImpl::LevelDBDatabaseImpl(
leveldb::mojom::LevelDBDatabaseRequest request,
std::unique_ptr<leveldb::Env> environment,
std::unique_ptr<leveldb::DB> db)
: binding_(this, std::move(request)),
environment_(std::move(environment)),
db_(std::move(db)) {}
LevelDBDatabaseImpl::~LevelDBDatabaseImpl() {
for (auto& p : iterator_map_)
delete p.second;
for (auto& p : snapshot_map_)
db_->ReleaseSnapshot(p.second);
}
void LevelDBDatabaseImpl::Put(mojo::Array<uint8_t> key,
mojo::Array<uint8_t> value,
const PutCallback& callback) {
leveldb::Status status =
db_->Put(leveldb::WriteOptions(), GetSliceFor(key), GetSliceFor(value));
callback.Run(LeveldbStatusToError(status));
}
void LevelDBDatabaseImpl::Delete(mojo::Array<uint8_t> key,
const DeleteCallback& callback) {
leveldb::Status status =
db_->Delete(leveldb::WriteOptions(), GetSliceFor(key));
callback.Run(LeveldbStatusToError(status));
}
void LevelDBDatabaseImpl::DeletePrefixed(
mojo::Array<uint8_t> key_prefix,
const DeletePrefixedCallback& callback) {
leveldb::WriteBatch batch;
leveldb::Status status = DeletePrefixedHelper(
GetSliceFor(key_prefix), &batch);
if (status.ok())
status = db_->Write(leveldb::WriteOptions(), &batch);
callback.Run(LeveldbStatusToError(status));
}
void LevelDBDatabaseImpl::Write(
mojo::Array<mojom::BatchedOperationPtr> operations,
const WriteCallback& callback) {
leveldb::WriteBatch batch;
for (size_t i = 0; i < operations.size(); ++i) {
switch (operations[i]->type) {
case mojom::BatchOperationType::PUT_KEY: {
batch.Put(GetSliceFor(operations[i]->key),
GetSliceFor(operations[i]->value));
break;
}
case mojom::BatchOperationType::DELETE_KEY: {
batch.Delete(GetSliceFor(operations[i]->key));
break;
}
case mojom::BatchOperationType::DELETE_PREFIXED_KEY: {
DeletePrefixedHelper(GetSliceFor(operations[i]->key), &batch);
break;
}
}
}
leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
callback.Run(LeveldbStatusToError(status));
}
void LevelDBDatabaseImpl::Get(mojo::Array<uint8_t> key,
const GetCallback& callback) {
std::string value;
leveldb::Status status =
db_->Get(leveldb::ReadOptions(), GetSliceFor(key), &value);
callback.Run(LeveldbStatusToError(status), mojo::Array<uint8_t>::From(value));
}
void LevelDBDatabaseImpl::GetPrefixed(mojo::Array<uint8_t> key_prefix,
const GetPrefixedCallback& callback) {
mojo::Array<mojom::KeyValuePtr> data;
leveldb::Status status = ForEachWithPrefix(
db_.get(), GetSliceFor(key_prefix),
[&data](const leveldb::Slice& key, const leveldb::Slice& value) {
mojom::KeyValuePtr kv = mojom::KeyValue::New();
kv->key = GetArrayFor(key);
kv->value = GetArrayFor(value);
data.push_back(std::move(kv));
});
callback.Run(LeveldbStatusToError(status), std::move(data));
}
void LevelDBDatabaseImpl::GetSnapshot(const GetSnapshotCallback& callback) {
const Snapshot* s = db_->GetSnapshot();
uint64_t new_id = GetSafeRandomId(snapshot_map_);
snapshot_map_.insert(std::make_pair(new_id, s));
callback.Run(new_id);
}
void LevelDBDatabaseImpl::ReleaseSnapshot(uint64_t snapshot_id) {
auto it = snapshot_map_.find(snapshot_id);
if (it != snapshot_map_.end()) {
db_->ReleaseSnapshot(it->second);
snapshot_map_.erase(it);
}
}
void LevelDBDatabaseImpl::GetFromSnapshot(uint64_t snapshot_id,
mojo::Array<uint8_t> key,
const GetCallback& callback) {
// If the snapshot id is invalid, send back invalid argument
auto it = snapshot_map_.find(snapshot_id);
if (it == snapshot_map_.end()) {
callback.Run(mojom::DatabaseError::INVALID_ARGUMENT,
mojo::Array<uint8_t>());
return;
}
std::string value;
leveldb::ReadOptions options;
options.snapshot = it->second;
leveldb::Status status = db_->Get(options, GetSliceFor(key), &value);
callback.Run(LeveldbStatusToError(status), mojo::Array<uint8_t>::From(value));
}
void LevelDBDatabaseImpl::NewIterator(const NewIteratorCallback& callback) {
Iterator* iterator = db_->NewIterator(leveldb::ReadOptions());
uint64_t new_id = GetSafeRandomId(iterator_map_);
iterator_map_.insert(std::make_pair(new_id, iterator));
callback.Run(new_id);
}
void LevelDBDatabaseImpl::NewIteratorFromSnapshot(
uint64_t snapshot_id,
const NewIteratorCallback& callback) {
// If the snapshot id is invalid, send back invalid argument
auto it = snapshot_map_.find(snapshot_id);
if (it == snapshot_map_.end()) {
callback.Run(0);
return;
}
leveldb::ReadOptions options;
options.snapshot = it->second;
Iterator* iterator = db_->NewIterator(options);
uint64_t new_id = GetSafeRandomId(iterator_map_);
iterator_map_.insert(std::make_pair(new_id, iterator));
callback.Run(new_id);
}
void LevelDBDatabaseImpl::ReleaseIterator(uint64_t iterator_id) {
auto it = iterator_map_.find(iterator_id);
if (it != iterator_map_.end()) {
delete it->second;
iterator_map_.erase(it);
}
}
void LevelDBDatabaseImpl::IteratorSeekToFirst(
uint64_t iterator_id,
const IteratorSeekToFirstCallback& callback) {
auto it = iterator_map_.find(iterator_id);
if (it == iterator_map_.end()) {
callback.Run(false, mojom::DatabaseError::INVALID_ARGUMENT, nullptr,
nullptr);
return;
}
it->second->SeekToFirst();
ReplyToIteratorMessage(it->second, callback);
}
void LevelDBDatabaseImpl::IteratorSeekToLast(
uint64_t iterator_id,
const IteratorSeekToLastCallback& callback) {
auto it = iterator_map_.find(iterator_id);
if (it == iterator_map_.end()) {
callback.Run(false, mojom::DatabaseError::INVALID_ARGUMENT, nullptr,
nullptr);
return;
}
it->second->SeekToLast();
ReplyToIteratorMessage(it->second, callback);
}
void LevelDBDatabaseImpl::IteratorSeek(
uint64_t iterator_id,
mojo::Array<uint8_t> target,
const IteratorSeekToLastCallback& callback) {
auto it = iterator_map_.find(iterator_id);
if (it == iterator_map_.end()) {
callback.Run(false, mojom::DatabaseError::INVALID_ARGUMENT, nullptr,
nullptr);
return;
}
it->second->Seek(GetSliceFor(target));
ReplyToIteratorMessage(it->second, callback);
}
void LevelDBDatabaseImpl::IteratorNext(uint64_t iterator_id,
const IteratorNextCallback& callback) {
auto it = iterator_map_.find(iterator_id);
if (it == iterator_map_.end()) {
callback.Run(false, mojom::DatabaseError::INVALID_ARGUMENT, nullptr,
nullptr);
return;
}
it->second->Next();
ReplyToIteratorMessage(it->second, callback);
}
void LevelDBDatabaseImpl::IteratorPrev(uint64_t iterator_id,
const IteratorPrevCallback& callback) {
auto it = iterator_map_.find(iterator_id);
if (it == iterator_map_.end()) {
callback.Run(false, mojom::DatabaseError::INVALID_ARGUMENT, nullptr,
nullptr);
return;
}
it->second->Prev();
ReplyToIteratorMessage(it->second, callback);
}
void LevelDBDatabaseImpl::ReplyToIteratorMessage(
leveldb::Iterator* it,
const IteratorSeekToFirstCallback& callback) {
if (!it->Valid()) {
callback.Run(false, LeveldbStatusToError(it->status()), nullptr, nullptr);
return;
}
callback.Run(true, LeveldbStatusToError(it->status()), GetArrayFor(it->key()),
GetArrayFor(it->value()));
}
leveldb::Status LevelDBDatabaseImpl::DeletePrefixedHelper(
const leveldb::Slice& key_prefix,
leveldb::WriteBatch* batch) {
leveldb::Status status = ForEachWithPrefix(db_.get(), key_prefix,
[batch](const leveldb::Slice& key, const leveldb::Slice& value) {
batch->Delete(key);
});
return status;
}
} // namespace leveldb