arnaucube
arnaucube/go-merkletree-iden3Public Self-hosted repository
go-merkletree-iden3/db/leveldb/leveldb.go
db/leveldb/leveldb.goRaw
  1package leveldb
  2
  3import (
  4	"github.com/iden3/go-merkletree/db"
  5	"github.com/syndtr/goleveldb/leveldb"
  6	"github.com/syndtr/goleveldb/leveldb/errors"
  7	"github.com/syndtr/goleveldb/leveldb/opt"
  8	"github.com/syndtr/goleveldb/leveldb/util"
  9)
 10
 11// Storage implements the db.Storage interface
 12type Storage struct {
 13	ldb    *leveldb.DB
 14	prefix []byte
 15}
 16
 17// StorageTx implements the db.Tx interface
 18type StorageTx struct {
 19	*Storage
 20	cache db.KvMap
 21}
 22
 23// NewLevelDbStorage returns a new Storage
 24func NewLevelDbStorage(path string, errorIfMissing bool) (*Storage, error) {
 25	o := &opt.Options{
 26		ErrorIfMissing: errorIfMissing,
 27	}
 28	ldb, err := leveldb.OpenFile(path, o)
 29	if err != nil {
 30		return nil, err
 31	}
 32	return &Storage{ldb, []byte{}}, nil
 33}
 34
 35// WithPrefix implements the method WithPrefix of the interface db.Storage
 36func (l *Storage) WithPrefix(prefix []byte) db.Storage {
 37	return &Storage{l.ldb, db.Concat(l.prefix, prefix)}
 38}
 39
 40// NewTx implements the method NewTx of the interface db.Storage
 41func (l *Storage) NewTx() (db.Tx, error) {
 42	return &StorageTx{l, make(db.KvMap)}, nil
 43}
 44
 45// Get retreives a value from a key in the db.Storage
 46func (l *Storage) Get(key []byte) ([]byte, error) {
 47	v, err := l.ldb.Get(db.Concat(l.prefix, key[:]), nil)
 48	if err == errors.ErrNotFound {
 49		return nil, db.ErrNotFound
 50	}
 51	return v, err
 52}
 53
 54// Iterate implements the method Iterate of the interface db.Storage
 55func (l *Storage) Iterate(f func([]byte, []byte) (bool, error)) error {
 56	// FIXME: Use the prefix!
 57	snapshot, err := l.ldb.GetSnapshot()
 58	if err != nil {
 59		return err
 60	}
 61	iter := snapshot.NewIterator(util.BytesPrefix(l.prefix), nil)
 62	defer iter.Release()
 63	for iter.Next() {
 64		localKey := iter.Key()[len(l.prefix):]
 65		if cont, err := f(localKey, iter.Value()); err != nil {
 66			return err
 67		} else if !cont {
 68			break
 69		}
 70	}
 71	iter.Release()
 72	return iter.Error()
 73}
 74
 75// Get retreives a value from a key in the interface db.Tx
 76func (tx *StorageTx) Get(key []byte) ([]byte, error) {
 77	var err error
 78
 79	fullkey := db.Concat(tx.prefix, key)
 80
 81	if value, ok := tx.cache.Get(fullkey); ok {
 82		return value, nil
 83	}
 84
 85	value, err := tx.ldb.Get(fullkey, nil)
 86	if err == errors.ErrNotFound {
 87		return nil, db.ErrNotFound
 88	}
 89
 90	return value, err
 91}
 92
 93// Put saves a key:value into the db.Storage
 94func (tx *StorageTx) Put(k, v []byte) error {
 95	tx.cache.Put(db.Concat(tx.prefix, k[:]), v)
 96	return nil
 97}
 98
 99// Add implements the method Add of the interface db.Tx
100func (tx *StorageTx) Add(atx db.Tx) error {
101	ldbtx := atx.(*StorageTx)
102	for _, v := range ldbtx.cache {
103		tx.cache.Put(v.K, v.V)
104	}
105	return nil
106}
107
108// Commit implements the method Commit of the interface db.Tx
109func (tx *StorageTx) Commit() error {
110	var batch leveldb.Batch
111	for _, v := range tx.cache {
112		batch.Put(v.K, v.V)
113	}
114
115	tx.cache = nil
116	return tx.ldb.Write(&batch, nil)
117}
118
119// Close implements the method Close of the interface db.Tx
120func (tx *StorageTx) Close() {
121	tx.cache = nil
122}
123
124// Close implements the method Close of the interface db.Storage
125func (l *Storage) Close() {
126	if err := l.ldb.Close(); err != nil {
127		panic(err)
128	}
129}
130
131// LevelDB is an extra method that returns the *leveldb.DB
132func (l *Storage) LevelDB() *leveldb.DB {
133	return l.ldb
134}
135
136// List implements the method List of the interface db.Storage
137func (l *Storage) List(limit int) ([]db.KV, error) {
138	ret := []db.KV{}
139	err := l.Iterate(func(key []byte, value []byte) (bool, error) {
140		ret = append(ret, db.KV{K: db.Clone(key), V: db.Clone(value)})
141		if len(ret) == limit {
142			return false, nil
143		}
144		return true, nil
145	})
146	return ret, err
147}