| // 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/bind.h" |
| #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::RewriteDB(RewriteDBCallback callback) { |
| 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(); |
| } |
| |
| void FakeLevelDBDatabase::FlushBindingsForTesting() { |
| bindings_.FlushForTesting(); |
| } |
| |
| 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 |