/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see .
*/
#include "LevelDB.h"
#include "Assertions.h"
namespace dev
{
namespace db
{
namespace
{
inline leveldb::Slice toLDBSlice(Slice _slice)
{
return leveldb::Slice(_slice.data(), _slice.size());
}
class LevelDBWriteBatch : public WriteBatchFace
{
public:
void insert(Slice _key, Slice _value) override;
void kill(Slice _key) override;
leveldb::WriteBatch const& writeBatch() const { return m_writeBatch; }
leveldb::WriteBatch& writeBatch() { return m_writeBatch; }
private:
leveldb::WriteBatch m_writeBatch;
};
void LevelDBWriteBatch::insert(Slice _key, Slice _value)
{
m_writeBatch.Put(toLDBSlice(_key), toLDBSlice(_value));
}
void LevelDBWriteBatch::kill(Slice _key)
{
m_writeBatch.Delete(toLDBSlice(_key));
}
} // namespace
leveldb::ReadOptions LevelDB::defaultReadOptions()
{
return leveldb::ReadOptions();
}
leveldb::WriteOptions LevelDB::defaultWriteOptions()
{
return leveldb::WriteOptions();
}
leveldb::Options LevelDB::defaultDBOptions()
{
leveldb::Options options;
options.create_if_missing = true;
options.max_open_files = 256;
return options;
}
LevelDB::LevelDB(boost::filesystem::path const& _path, leveldb::ReadOptions _readOptions,
leveldb::WriteOptions _writeOptions, leveldb::Options _dbOptions)
: m_db(nullptr), m_readOptions(std::move(_readOptions)), m_writeOptions(std::move(_writeOptions))
{
std::string const path = _path.string();
auto db = static_cast(nullptr);
auto const status = leveldb::DB::Open(_dbOptions, path, &db);
if (!status.ok())
{
std::string const statusStr = status.ToString();
if (status.IsCorruption())
BOOST_THROW_EXCEPTION(DBCorruption() << errinfo_db(statusStr) << errinfo_path(path));
else if (status.IsIOError())
BOOST_THROW_EXCEPTION(DBIOError() << errinfo_db(statusStr) << errinfo_path(path));
else
BOOST_THROW_EXCEPTION(FailedToOpenDB() << errinfo_db(statusStr) << errinfo_path(path));
}
assert(db);
m_db.reset(db);
}
std::string LevelDB::lookup(Slice _key) const
{
leveldb::Slice const key(_key.data(), _key.size());
std::string value;
auto const status = m_db->Get(m_readOptions, key, &value);
if (!status.ok())
{
BOOST_THROW_EXCEPTION(FailedLookupInDB() << errinfo_db(status.ToString()));
}
return value;
}
bool LevelDB::exists(Slice _key) const
{
std::string value;
leveldb::Slice const key(_key.data(), _key.size());
auto const status = m_db->Get(m_readOptions, key, &value);
return status.ok();
}
void LevelDB::insert(Slice _key, Slice _value)
{
leveldb::Slice const key(_key.data(), _key.size());
leveldb::Slice const value(_value.data(), _value.size());
auto const status = m_db->Put(m_writeOptions, key, value);
if (!status.ok())
{
BOOST_THROW_EXCEPTION(FailedInsertInDB() << errinfo_db(status.ToString()));
}
}
void LevelDB::kill(Slice _key)
{
leveldb::Slice const key(_key.data(), _key.size());
auto const status = m_db->Delete(m_writeOptions, key);
if (!status.ok())
{
BOOST_THROW_EXCEPTION(FailedDeleteInDB() << errinfo_db(status.ToString()));
}
}
std::unique_ptr LevelDB::createWriteBatch() const
{
return std::unique_ptr(new LevelDBWriteBatch());
}
void LevelDB::commit(std::unique_ptr _batch)
{
if (!_batch)
{
BOOST_THROW_EXCEPTION(FailedCommitInDB() << errinfo_comment("Cannot commit null batch"));
}
auto* batchPtr = dynamic_cast(_batch.get());
if (!batchPtr)
{
BOOST_THROW_EXCEPTION(
FailedCommitInDB() << errinfo_comment("Invalid batch type passed to LevelDB::commit"));
}
auto const status = m_db->Write(m_writeOptions, &batchPtr->writeBatch());
if (!status.ok())
{
BOOST_THROW_EXCEPTION(FailedCommitInDB() << errinfo_db(status.ToString()));
}
}
void LevelDB::forEach(std::function f) const
{
std::unique_ptr itr(m_db->NewIterator(m_readOptions));
if (itr == nullptr)
{
BOOST_THROW_EXCEPTION(FailedIterateDB() << errinfo_comment("null iterator"));
}
auto keepIterating = true;
for (itr->SeekToFirst(); keepIterating && itr->Valid(); itr->Next())
{
auto const dbKey = itr->key();
auto const dbValue = itr->value();
Slice const key(dbKey.data(), dbKey.size());
Slice const value(dbValue.data(), dbValue.size());
keepIterating = f(key, value);
}
}
} // namespace db
} // namespace dev