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}