From 43b7905e98d827df45e970dbaf5f08561ecc6cda Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 3 Sep 2012 19:05:30 +0200 Subject: [PATCH] LevelDB glue Database-independent glue for supporting LevelDB databases. Based on code from earlier commits by Mike Hearn in his leveldb branch. --- bitcoin-qt.pro | 1 + src/leveldb.cpp | 58 ++++++++++++++++ src/leveldb.h | 144 +++++++++++++++++++++++++++++++++++++++ src/makefile.linux-mingw | 5 +- src/makefile.mingw | 3 +- src/makefile.osx | 3 +- src/makefile.unix | 3 +- 7 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 src/leveldb.cpp create mode 100644 src/leveldb.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 68add911..c9297166 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -97,6 +97,7 @@ contains(USE_LEVELDB, -) { DEFINES += USE_LEVELDB INCLUDEPATH += src/leveldb/include src/leveldb/helpers LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a + SOURCES += src/leveldb.cpp !windows { genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a } else { diff --git a/src/leveldb.cpp b/src/leveldb.cpp new file mode 100644 index 00000000..29e5e6a7 --- /dev/null +++ b/src/leveldb.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "leveldb.h" +#include "util.h" + +#include +#include +#include + +#include + +static leveldb::Options GetOptions() { + leveldb::Options options; + int nCacheSizeMB = GetArg("-dbcache", 25); + options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.compression = leveldb::kNoCompression; + return options; +} + +CLevelDB::CLevelDB(const boost::filesystem::path &path) { + penv = NULL; + readoptions.verify_checksums = true; + iteroptions.verify_checksums = true; + iteroptions.fill_cache = false; + syncoptions.sync = true; + options = GetOptions(); + options.create_if_missing = true; + boost::filesystem::create_directory(path); + printf("Opening LevelDB in %s\n", path.string().c_str()); + leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + if (!status.ok()) + throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); + printf("Opened LevelDB successfully\n"); +} + +CLevelDB::~CLevelDB() { + delete pdb; + pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete penv; + options.env = NULL; +} + +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) { + leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); + if (!status.ok()) { + printf("LevelDB write failure: %s\n", status.ToString().c_str()); + return false; + } + return true; +} + diff --git a/src/leveldb.h b/src/leveldb.h new file mode 100644 index 00000000..28484dac --- /dev/null +++ b/src/leveldb.h @@ -0,0 +1,144 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_LEVELDB_H +#define BITCOIN_LEVELDB_H + +#include "serialize.h" + +#include +#include + +#include + +// Batch of changes queued to be written to a CLevelDB +class CLevelDBBatch +{ + friend class CLevelDB; + +private: + leveldb::WriteBatch batch; + +public: + template void Write(const K& key, const V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(value)); + ssValue << value; + leveldb::Slice slValue(&ssValue[0], ssValue.size()); + + batch.Put(slKey, slValue); + } + + template void Erase(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + batch.Delete(slKey); + } +}; + +class CLevelDB +{ +private: + // custom environment this database is using (may be NULL in case of default environment) + leveldb::Env *penv; + + // database options used + leveldb::Options options; + + // options used when reading from the database + leveldb::ReadOptions readoptions; + + // options used when iterating over values of the database + leveldb::ReadOptions iteroptions; + + // options used when writing to the database + leveldb::WriteOptions writeoptions; + + // options used when sync writing to the database + leveldb::WriteOptions syncoptions; + + // the database itself + leveldb::DB *pdb; + +public: + CLevelDB(const boost::filesystem::path &path); + ~CLevelDB(); + + template bool Read(const K& key, V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + } + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch(std::exception &e) { + return false; + } + return true; + } + + template bool Write(const K& key, const V& value, bool fSync = false) { + CLevelDBBatch batch; + batch.Write(key, value); + return WriteBatch(batch, fSync); + } + + template bool Exists(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + } + return true; + } + + template bool Erase(const K& key, bool fSync = false) { + CLevelDBBatch batch; + batch.Erase(key); + return WriteBatch(batch, fSync); + } + + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false); + + // not available for LevelDB; provide for compatibility with BDB + bool Flush() { + return true; + } + + bool Sync() { + CLevelDBBatch batch; + return WriteBatch(batch, true); + } + + // not exactly clean encapsulation, but it's easiest for now + leveldb::Iterator *NewIterator() { + return pdb->NewIterator(iteroptions); + } +}; + +#endif // BITCOIN_LEVELDB_H + \ No newline at end of file diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 8f8279c9..9bad54cb 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -88,12 +88,13 @@ OBJS= \ all: bitcoind.exe ifdef USE_LEVELDB -LIBS += $(CURDIR)/leveldb/libleveldb.lib $(CURDIR)/leveldb/libmemenv.a +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += -I"$(CURDIR)/leveldb/include" -DUSE_LEVELDB DEFS += -I"$(CURDIR)/leveldb/helpers" +OBJS += obj/leveldb.o leveldb/libleveldb.a: @echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd .. -obj/db.o: leveldb/libleveldb.a +obj/leveldb.o: leveldb/libleveldb.a endif obj/build.h: FORCE diff --git a/src/makefile.mingw b/src/makefile.mingw index e2560d99..3953af3b 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -94,9 +94,10 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/leveldb.o leveldb/libleveldb.a: cd leveldb; make libleveldb.a libmemenv.a; cd .. -obj/db.o: leveldb/libleveldb.lib +obj/leveldb.o: leveldb/libleveldb.lib endif obj/%.o: %.cpp $(HEADERS) diff --git a/src/makefile.osx b/src/makefile.osx index bfda7884..61823652 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -127,9 +127,10 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/leveldb.o leveldb/libleveldb.a: @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd .. -obj/db.o: leveldb/libleveldb.a +obj/leveldb.o: leveldb/libleveldb.a endif # auto-generated dependencies: diff --git a/src/makefile.unix b/src/makefile.unix index 01b637e9..3fcefa1e 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -143,9 +143,10 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +OBJS += obj/leveldb.o leveldb/libleveldb.a: @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; -obj/db.o: leveldb/libleveldb.a +obj/leveldb.o: leveldb/libleveldb.a endif # auto-generated dependencies: