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() bool CLevelDBWrapper::IsEmpty()
{ {
boost::scoped_ptr<leveldb::Iterator> it(NewIterator()); boost::scoped_ptr<CLevelDBIterator> it(NewIterator());
it->SeekToFirst(); it->SeekToFirst();
return !(it->Valid()); return !(it->Valid());
} }
@ -145,3 +145,10 @@ std::string CLevelDBWrapper::GetObfuscateKeyHex() const
{ {
return HexStr(obfuscate_key); 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

@ -68,7 +68,65 @@ public:
batch.Delete(slKey); batch.Delete(slKey);
} }
}; };
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 class CLevelDBWrapper
{ {
private: private:
@ -191,11 +249,10 @@ public:
return WriteBatch(batch, true); return WriteBatch(batch, true);
} }
// not exactly clean encapsulation, but it's easiest for now CLevelDBIterator *NewIterator()
leveldb::Iterator* NewIterator() {
return new CLevelDBIterator(pdb->NewIterator(iteroptions));
{ {
return pdb->NewIterator(iteroptions);
}
/** /**
* Return true if the database managed by this class contains no entries. * 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 /* 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 only need read operations on it, use a const-cast to get around
that restriction. */ that restriction. */
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator()); boost::scoped_ptr<CLevelDBWrapper> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst(); pcursor->Seek('c');
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = GetBestBlock(); stats.hashBlock = GetBestBlock();
@ -107,22 +107,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
CAmount nTotalAmount = 0; CAmount nTotalAmount = 0;
while (pcursor->Valid()) { while (pcursor->Valid()) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
try { std::pair<char, uint256> key;
leveldb::Slice slKey = pcursor->key(); CCoins coins;
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); if (pcursor->GetKey(key) && key.first == 'c') {
char chType; if (pcursor->GetValue(coins)) {
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);
stats.nTransactions++; stats.nTransactions++;
for (unsigned int i=0; i<coins.vout.size(); i++) { for (unsigned int i=0; i<coins.vout.size(); i++) {
const CTxOut &out = coins.vout[i]; const CTxOut &out = coins.vout[i];
@ -133,13 +121,15 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
nTotalAmount += out.nValue; nTotalAmount += out.nValue;
} }
} }
stats.nSerializedSize += 32 + slValue.size(); stats.nSerializedSize += 32 + pcursor->GetKeySize();
ss << VARINT(0); ss << VARINT(0);
} else {
return error("CCoinsViewDB::GetStats() : unable to read value");
} }
pcursor->Next(); } else {
} catch (const std::exception& e) { break;
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
} }
pcursor->Next();
} }
{ {
LOCK(cs_main); LOCK(cs_main);
@ -189,24 +179,15 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
{ {
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); pcursor->Seek(make_pair('b', uint256(0)));
ssKeySet << make_pair(DB_BLOCK_INDEX, uint256());
pcursor->Seek(ssKeySet.str());
// Load mapBlockIndex // Load mapBlockIndex
while (pcursor->Valid()) { while (pcursor->Valid()) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
try { std::pair<char, uint256> key;
leveldb::Slice slKey = pcursor->key(); if (pcursor->GetKey(key) && key.first == 'b') {
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); CDiskBlockIndex diskindex;
char chType; if (pcursor->GetValue(diskindex)) {
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;
// Construct block index object // Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
@ -227,10 +208,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pcursor->Next(); pcursor->Next();
} else { } else {
break; // if shutdown requested or finished loading block index return error("LoadBlockIndex() : failed to read value");
} }
} catch (const std::exception& e) { } else {
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); break;
} }
} }