Encapsulate CLevelDB iterators cleanly

Conflicts:
	src/leveldb.cpp
	src/leveldb.h
	src/txdb.cpp
This commit is contained in:
Pieter Wuille 2015-10-07 17:12:24 -07:00 committed by James O'Beirne
parent 4fac576c61
commit 3499ce1e1a
3 changed files with 89 additions and 44 deletions

View File

@ -131,7 +131,7 @@ std::vector<unsigned char> CLevelDBWrapper::CreateObfuscateKey() const
bool CLevelDBWrapper::IsEmpty()
{
boost::scoped_ptr<leveldb::Iterator> it(NewIterator());
boost::scoped_ptr<CLevelDBIterator> it(NewIterator());
it->SeekToFirst();
return !(it->Valid());
}
@ -145,3 +145,10 @@ std::string CLevelDBWrapper::GetObfuscateKeyHex() const
{
return HexStr(obfuscate_key);
}
CLevelDBIterator::~CLevelDBIterator() { delete piter; }
bool CLevelDBIterator::Valid() { return piter->Valid(); }
void CLevelDBIterator::SeekToFirst() { piter->SeekToFirst(); }
void CLevelDBIterator::SeekToLast() { piter->SeekToLast(); }
void CLevelDBIterator::Next() { piter->Next(); }
void CLevelDBIterator::Prev() { piter->Prev(); }

View File

@ -69,6 +69,64 @@ public:
}
};
class CLevelDBIterator
{
private:
leveldb::Iterator *piter;
public:
CLevelDBIterator(leveldb::Iterator *piterIn) : piter(piterIn) {}
~CLevelDBIterator();
bool Valid();
void SeekToFirst();
void SeekToLast();
template<typename K> void Seek(const K& key) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
piter->Seek(slKey);
}
void Next();
void Prev();
template<typename K> bool GetKey(K& key) {
leveldb::Slice slKey = piter->key();
try {
CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
ssKey >> key;
} catch(std::exception &e) {
return false;
}
return true;
}
unsigned int GetKeySize() {
return piter->key().size();
}
template<typename V> bool GetValue(V& value) {
leveldb::Slice slValue = piter->value();
try {
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
ssValue.Xor(db.GetObfuscateKey());
ssValue >> value;
} catch(std::exception &e) {
return false;
}
return true;
}
unsigned int GetValueSize() {
return piter->value().size();
}
};
class CLevelDBWrapper
{
private:
@ -191,11 +249,10 @@ public:
return WriteBatch(batch, true);
}
// not exactly clean encapsulation, but it's easiest for now
leveldb::Iterator* NewIterator()
CLevelDBIterator *NewIterator()
{
return new CLevelDBIterator(pdb->NewIterator(iteroptions));
{
return pdb->NewIterator(iteroptions);
}
/**
* Return true if the database managed by this class contains no entries.

View File

@ -98,8 +98,8 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
/* It seems that there are no "const iterators" for LevelDB. Since we
only need read operations on it, use a const-cast to get around
that restriction. */
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst();
boost::scoped_ptr<CLevelDBWrapper> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->Seek('c');
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = GetBestBlock();
@ -107,22 +107,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
CAmount nTotalAmount = 0;
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
try {
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType;
ssKey >> chType;
if (chType == DB_COINS) {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CCoins coins;
ssValue >> coins;
uint256 txhash;
ssKey >> txhash;
ss << txhash;
ss << VARINT(coins.nVersion);
ss << (coins.fCoinBase ? 'c' : 'n');
ss << VARINT(coins.nHeight);
std::pair<char, uint256> key;
CCoins coins;
if (pcursor->GetKey(key) && key.first == 'c') {
if (pcursor->GetValue(coins)) {
stats.nTransactions++;
for (unsigned int i=0; i<coins.vout.size(); i++) {
const CTxOut &out = coins.vout[i];
@ -133,13 +121,15 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
nTotalAmount += out.nValue;
}
}
stats.nSerializedSize += 32 + slValue.size();
stats.nSerializedSize += 32 + pcursor->GetKeySize();
ss << VARINT(0);
} else {
return error("CCoinsViewDB::GetStats() : unable to read value");
}
pcursor->Next();
} catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
} else {
break;
}
pcursor->Next();
}
{
LOCK(cs_main);
@ -189,24 +179,15 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
{
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
ssKeySet << make_pair(DB_BLOCK_INDEX, uint256());
pcursor->Seek(ssKeySet.str());
pcursor->Seek(make_pair('b', uint256(0)));
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
try {
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType;
ssKey >> chType;
if (chType == DB_BLOCK_INDEX) {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockIndex diskindex;
ssValue >> diskindex;
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == 'b') {
CDiskBlockIndex diskindex;
if (pcursor->GetValue(diskindex)) {
// Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
@ -227,10 +208,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pcursor->Next();
} else {
break; // if shutdown requested or finished loading block index
return error("LoadBlockIndex() : failed to read value");
}
} catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
} else {
break;
}
}