changeset 3639:61a0d2ebf491 draft

LevelDB glue Database-independent glue for supporting LevelDB databases. Based on code from earlier commits by Mike Hearn in his leveldb branch.
author Pieter Wuille <pieter.wuille@gmail.com>
date Mon, 03 Sep 2012 19:05:30 +0200
parents 4c31c15d73af
children 195f5ad43476
files bitcoin-qt.pro src/leveldb.cpp src/leveldb.h src/makefile.linux-mingw src/makefile.mingw src/makefile.osx src/makefile.unix
diffstat 7 files changed, 212 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -97,6 +97,7 @@
     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 {
new file mode 100644
--- /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 <leveldb/env.h>
+#include <leveldb/cache.h>
+#include <leveldb/filter_policy.h>
+
+#include <boost/filesystem.hpp>
+
+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;
+}
+
new file mode 100644
--- /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 <leveldb/db.h>
+#include <leveldb/write_batch.h>
+
+#include <boost/filesystem/path.hpp>
+
+// Batch of changes queued to be written to a CLevelDB
+class CLevelDBBatch
+{
+    friend class CLevelDB;
+
+private:
+    leveldb::WriteBatch batch;
+
+public:
+    template<typename K, typename V> 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<typename K> 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<typename K, typename V> 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<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) {
+        CLevelDBBatch batch;
+        batch.Write(key, value);
+        return WriteBatch(batch, fSync);
+    }
+
+    template<typename K> 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<typename K> 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
--- a/src/makefile.linux-mingw
+++ b/src/makefile.linux-mingw
@@ -88,12 +88,13 @@
 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
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -94,9 +94,10 @@
 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)
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -127,9 +127,10 @@
 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:
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -143,9 +143,10 @@
 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: