+package db
+
+import (
+ "fmt"
+ "path"
+
+ "github.com/syndtr/goleveldb/leveldb"
+ "github.com/syndtr/goleveldb/leveldb/errors"
+ "github.com/syndtr/goleveldb/leveldb/iterator"
+ "github.com/syndtr/goleveldb/leveldb/opt"
+ "github.com/syndtr/goleveldb/leveldb/util"
+
+ . "github.com/tendermint/tmlibs/common"
+)
+
+func init() {
+ dbCreator := func(name string, dir string) (DB, error) {
+ return NewGoLevelDB(name, dir)
+ }
+ registerDBCreator(LevelDBBackendStr, dbCreator, false)
+ registerDBCreator(GoLevelDBBackendStr, dbCreator, false)
+}
+
+type GoLevelDB struct {
+ db *leveldb.DB
+}
+
+func NewGoLevelDB(name string, dir string) (*GoLevelDB, error) {
+ dbPath := path.Join(dir, name+".db")
+ db, err := leveldb.OpenFile(dbPath, nil)
+ if err != nil {
+ return nil, err
+ }
+ database := &GoLevelDB{db: db}
+ return database, nil
+}
+
+func (db *GoLevelDB) Get(key []byte) []byte {
+ res, err := db.db.Get(key, nil)
+ if err != nil {
+ if err == errors.ErrNotFound {
+ return nil
+ } else {
+ PanicCrisis(err)
+ }
+ }
+ return res
+}
+
+func (db *GoLevelDB) Set(key []byte, value []byte) {
+ err := db.db.Put(key, value, nil)
+ if err != nil {
+ PanicCrisis(err)
+ }
+}
+
+func (db *GoLevelDB) SetSync(key []byte, value []byte) {
+ err := db.db.Put(key, value, &opt.WriteOptions{Sync: true})
+ if err != nil {
+ PanicCrisis(err)
+ }
+}
+
+func (db *GoLevelDB) Delete(key []byte) {
+ err := db.db.Delete(key, nil)
+ if err != nil {
+ PanicCrisis(err)
+ }
+}
+
+func (db *GoLevelDB) DeleteSync(key []byte) {
+ err := db.db.Delete(key, &opt.WriteOptions{Sync: true})
+ if err != nil {
+ PanicCrisis(err)
+ }
+}
+
+func (db *GoLevelDB) DB() *leveldb.DB {
+ return db.db
+}
+
+func (db *GoLevelDB) Close() {
+ db.db.Close()
+}
+
+func (db *GoLevelDB) Print() {
+ str, _ := db.db.GetProperty("leveldb.stats")
+ fmt.Printf("%v\n", str)
+
+ iter := db.db.NewIterator(nil, nil)
+ for iter.Next() {
+ key := iter.Key()
+ value := iter.Value()
+ fmt.Printf("[%X]:\t[%X]\n", key, value)
+ }
+}
+
+func (db *GoLevelDB) Stats() map[string]string {
+ keys := []string{
+ "leveldb.num-files-at-level{n}",
+ "leveldb.stats",
+ "leveldb.sstables",
+ "leveldb.blockpool",
+ "leveldb.cachedblock",
+ "leveldb.openedtables",
+ "leveldb.alivesnaps",
+ "leveldb.aliveiters",
+ }
+
+ stats := make(map[string]string)
+ for _, key := range keys {
+ str, err := db.db.GetProperty(key)
+ if err == nil {
+ stats[key] = str
+ }
+ }
+ return stats
+}
+
+type goLevelDBIterator struct {
+ source iterator.Iterator
+}
+
+// Key returns a copy of the current key.
+func (it *goLevelDBIterator) Key() []byte {
+ key := it.source.Key()
+ k := make([]byte, len(key))
+ copy(k, key)
+
+ return k
+}
+
+// Value returns a copy of the current value.
+func (it *goLevelDBIterator) Value() []byte {
+ val := it.source.Value()
+ v := make([]byte, len(val))
+ copy(v, val)
+
+ return v
+}
+
+func (it *goLevelDBIterator) Seek(point []byte) bool {
+ return it.source.Seek(point)
+}
+
+func (it *goLevelDBIterator) Error() error {
+ return it.source.Error()
+}
+
+func (it *goLevelDBIterator) Next() bool {
+ return it.source.Next()
+}
+
+func (it *goLevelDBIterator) Release() {
+ it.source.Release()
+}
+
+func (db *GoLevelDB) Iterator() Iterator {
+ return &goLevelDBIterator{db.db.NewIterator(nil, nil)}
+}
+
+func (db *GoLevelDB) IteratorPrefix(prefix []byte) Iterator {
+ return &goLevelDBIterator{db.db.NewIterator(util.BytesPrefix(prefix), nil)}
+}
+
+func (db *GoLevelDB) NewBatch() Batch {
+ batch := new(leveldb.Batch)
+ return &goLevelDBBatch{db, batch}
+}
+
+//--------------------------------------------------------------------------------
+
+type goLevelDBBatch struct {
+ db *GoLevelDB
+ batch *leveldb.Batch
+}
+
+func (mBatch *goLevelDBBatch) Set(key, value []byte) {
+ mBatch.batch.Put(key, value)
+}
+
+func (mBatch *goLevelDBBatch) Delete(key []byte) {
+ mBatch.batch.Delete(key)
+}
+
+func (mBatch *goLevelDBBatch) Write() {
+ err := mBatch.db.db.Write(mBatch.batch, nil)
+ if err != nil {
+ PanicCrisis(err)
+ }
+}