blob: d7158a629f860b64ce0383a882af9f52859427c2 [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 "content/test/fake_leveldb_database.h"
#include <iterator>
#include <utility>
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
namespace content {
namespace {
using leveldb::mojom::BatchedOperation;
using leveldb::mojom::BatchedOperationPtr;
leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key,
std::vector<uint8_t> value) {
leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New();
result->key = std::move(key);
result->value = std::move(value);
return result;
}
base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) {
return base::StringPiece(reinterpret_cast<const char*>(data.data()),
data.size());
}
bool StartsWith(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& prefix) {
return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix),
base::CompareCase::SENSITIVE);
}
std::vector<uint8_t> successor(std::vector<uint8_t> data) {
for (unsigned i = data.size(); i > 0; --i) {
if (data[i - 1] < 255) {
data[i - 1]++;
return data;
}
}
NOTREACHED();
return data;
}
} // namespace
FakeLevelDBDatabase::FakeLevelDBDatabase(
std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
: mock_data_(*mock_data) {}
FakeLevelDBDatabase::~FakeLevelDBDatabase() {}
void FakeLevelDBDatabase::Bind(leveldb::mojom::LevelDBDatabaseRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void FakeLevelDBDatabase::Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
PutCallback callback) {
mock_data_[key] = value;
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Delete(const std::vector<uint8_t>& key,
DeleteCallback callback) {
mock_data_.erase(key);
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::DeletePrefixed(const std::vector<uint8_t>& key_prefix,
DeletePrefixedCallback callback) {
mock_data_.erase(mock_data_.lower_bound(key_prefix),
mock_data_.lower_bound(successor(key_prefix)));
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Write(
std::vector<leveldb::mojom::BatchedOperationPtr> operations,
WriteCallback callback) {
// Replace prefix delete and prefix copy with operations first, and then
// execute all operations. This models how the leveldb WriteBatch works.
for (size_t i = 0; i < operations.size(); ++i) {
const auto& op = operations[i];
switch (op->type) {
case leveldb::mojom::BatchOperationType::PUT_KEY:
break;
case leveldb::mojom::BatchOperationType::DELETE_KEY:
break;
case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY: {
std::vector<leveldb::mojom::BatchedOperationPtr> changes;
for (auto map_it = mock_data_.lower_bound(op->key);
map_it != mock_data_.lower_bound(successor(op->key)); ++map_it) {
BatchedOperationPtr item = BatchedOperation::New();
item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
item->key = map_it->first;
changes.push_back(std::move(item));
}
size_t diff = changes.size();
operations.insert(operations.begin() + i,
std::make_move_iterator(changes.begin()),
std::make_move_iterator(changes.end()));
i += diff;
continue;
}
case leveldb::mojom::BatchOperationType::COPY_PREFIXED_KEY: {
DCHECK(op->value);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
copy_changes = CopyPrefixedHelper(op->key, *op->value);
std::vector<leveldb::mojom::BatchedOperationPtr> changes;
for (auto& change : copy_changes) {
BatchedOperationPtr item = BatchedOperation::New();
item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
item->key = std::move(change.first);
item->value = std::move(change.second);
changes.push_back(std::move(item));
}
size_t diff = changes.size();
operations.insert(operations.begin() + i,
std::make_move_iterator(changes.begin()),
std::make_move_iterator(changes.end()));
i += diff;
continue;
}
}
}
for (const auto& op : operations) {
switch (op->type) {
case leveldb::mojom::BatchOperationType::PUT_KEY:
DCHECK(op->value);
mock_data_[op->key] = *op->value;
break;
case leveldb::mojom::BatchOperationType::DELETE_KEY:
mock_data_.erase(op->key);
break;
case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY:
break;
case leveldb::mojom::BatchOperationType::COPY_PREFIXED_KEY:
break;
}
}
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Get(const std::vector<uint8_t>& key,
GetCallback callback) {
if (mock_data_.find(key) != mock_data_.end()) {
std::move(callback).Run(leveldb::mojom::DatabaseError::OK, mock_data_[key]);
} else {
std::move(callback).Run(leveldb::mojom::DatabaseError::NOT_FOUND,
std::vector<uint8_t>());
}
}
void FakeLevelDBDatabase::GetPrefixed(const std::vector<uint8_t>& key_prefix,
GetPrefixedCallback callback) {
std::vector<leveldb::mojom::KeyValuePtr> data;
for (const auto& row : mock_data_) {
if (StartsWith(row.first, key_prefix)) {
data.push_back(CreateKeyValue(row.first, row.second));
}
}
std::move(callback).Run(leveldb::mojom::DatabaseError::OK, std::move(data));
}
void FakeLevelDBDatabase::CopyPrefixed(
const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix,
CopyPrefixedCallback callback) {
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> changes =
CopyPrefixedHelper(source_key_prefix, destination_key_prefix);
mock_data_.insert(changes.begin(), changes.end());
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::GetSnapshot(GetSnapshotCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::ReleaseSnapshot(
const base::UnguessableToken& snapshot) {
NOTREACHED();
}
void FakeLevelDBDatabase::GetFromSnapshot(
const base::UnguessableToken& snapshot,
const std::vector<uint8_t>& key,
GetCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::NewIterator(NewIteratorCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::NewIteratorFromSnapshot(
const base::UnguessableToken& snapshot,
NewIteratorFromSnapshotCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::ReleaseIterator(
const base::UnguessableToken& iterator) {
NOTREACHED();
}
void FakeLevelDBDatabase::IteratorSeekToFirst(
const base::UnguessableToken& iterator,
IteratorSeekToFirstCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::IteratorSeekToLast(
const base::UnguessableToken& iterator,
IteratorSeekToLastCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::IteratorSeek(const base::UnguessableToken& iterator,
const std::vector<uint8_t>& target,
IteratorSeekToLastCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::IteratorNext(const base::UnguessableToken& iterator,
IteratorNextCallback callback) {
NOTREACHED();
}
void FakeLevelDBDatabase::IteratorPrev(const base::UnguessableToken& iterator,
IteratorPrevCallback callback) {
NOTREACHED();
}
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
FakeLevelDBDatabase::CopyPrefixedHelper(
const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix) {
size_t source_key_prefix_size = source_key_prefix.size();
size_t destination_key_prefix_size = destination_key_prefix.size();
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
write_batch;
for (auto map_it = mock_data_.lower_bound(source_key_prefix);
map_it != mock_data_.lower_bound(successor(source_key_prefix));
++map_it) {
size_t excess_key = map_it->first.size() - source_key_prefix_size;
std::vector<uint8_t> new_key(destination_key_prefix_size + excess_key);
std::copy(destination_key_prefix.begin(), destination_key_prefix.end(),
new_key.begin());
std::copy(map_it->first.begin() + source_key_prefix_size,
map_it->first.end(),
new_key.begin() + destination_key_prefix_size);
write_batch.emplace_back(std::move(new_key), map_it->second);
}
return write_batch;
}
} // namespace content