storage: generalize Level/Bolt seek implementations
Too much in common. Just refactoring. no functional changes.
This commit is contained in:
parent
a5f8b8870a
commit
51b804ab0e
3 changed files with 51 additions and 60 deletions
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
|
@ -110,44 +109,30 @@ func (s *BoltDBStore) PutChangeSet(puts map[string][]byte) error {
|
|||
|
||||
// Seek implements the Store interface.
|
||||
func (s *BoltDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||
start := make([]byte, len(rng.Prefix)+len(rng.Start))
|
||||
copy(start, rng.Prefix)
|
||||
copy(start[len(rng.Prefix):], rng.Start)
|
||||
if rng.Backwards {
|
||||
s.seekBackwards(rng.Prefix, start, f)
|
||||
} else {
|
||||
s.seek(rng.Prefix, start, f)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BoltDBStore) seek(key []byte, start []byte, f func(k, v []byte) bool) {
|
||||
prefix := util.BytesPrefix(key)
|
||||
prefix.Start = start
|
||||
rang := seekRangeToPrefixes(rng)
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
var (
|
||||
k, v []byte
|
||||
next func() ([]byte, []byte)
|
||||
)
|
||||
|
||||
c := tx.Bucket(Bucket).Cursor()
|
||||
for k, v := c.Seek(prefix.Start); k != nil && (len(prefix.Limit) == 0 || bytes.Compare(k, prefix.Limit) <= 0); k, v = c.Next() {
|
||||
if !f(k, v) {
|
||||
break
|
||||
|
||||
if !rng.Backwards {
|
||||
k, v = c.Seek(rang.Start)
|
||||
next = c.Next
|
||||
} else {
|
||||
if len(rang.Limit) == 0 {
|
||||
lastKey, _ := c.Last()
|
||||
k, v = c.Seek(lastKey)
|
||||
} else {
|
||||
c.Seek(rang.Limit)
|
||||
k, v = c.Prev()
|
||||
}
|
||||
next = c.Prev
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BoltDBStore) seekBackwards(key []byte, start []byte, f func(k, v []byte) bool) {
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
c := tx.Bucket(Bucket).Cursor()
|
||||
// Move cursor to the first kv pair which is followed by the pair matching the specified prefix.
|
||||
if len(start) == 0 {
|
||||
lastKey, _ := c.Last()
|
||||
start = lastKey
|
||||
}
|
||||
rng := util.BytesPrefix(start) // in fact, we only need limit based on start slice to iterate backwards starting from this limit
|
||||
c.Seek(rng.Limit)
|
||||
for k, v := c.Prev(); k != nil && bytes.HasPrefix(k, key); k, v = c.Prev() {
|
||||
for ; k != nil && bytes.HasPrefix(k, rng.Prefix) && (len(rang.Limit) == 0 || bytes.Compare(k, rang.Limit) <= 0); k, v = next() {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
// LevelDBOptions configuration for LevelDB.
|
||||
|
@ -83,34 +82,21 @@ func (s *LevelDBStore) PutChangeSet(puts map[string][]byte) error {
|
|||
|
||||
// Seek implements the Store interface.
|
||||
func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||
start := make([]byte, len(rng.Prefix)+len(rng.Start))
|
||||
copy(start, rng.Prefix)
|
||||
copy(start[len(rng.Prefix):], rng.Start)
|
||||
if rng.Backwards {
|
||||
s.seekBackwards(rng.Prefix, start, f)
|
||||
var (
|
||||
next func() bool
|
||||
ok bool
|
||||
iter = s.db.NewIterator(seekRangeToPrefixes(rng), nil)
|
||||
)
|
||||
|
||||
if !rng.Backwards {
|
||||
ok = iter.Next()
|
||||
next = iter.Next
|
||||
} else {
|
||||
s.seek(rng.Prefix, start, f)
|
||||
ok = iter.Last()
|
||||
next = iter.Prev
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LevelDBStore) seek(key []byte, start []byte, f func(k, v []byte) bool) {
|
||||
prefix := util.BytesPrefix(key)
|
||||
prefix.Start = start
|
||||
iter := s.db.NewIterator(prefix, nil)
|
||||
for iter.Next() {
|
||||
if !f(iter.Key(), iter.Value()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iter.Release()
|
||||
}
|
||||
|
||||
func (s *LevelDBStore) seekBackwards(key []byte, start []byte, f func(k, v []byte) bool) {
|
||||
iRange := util.BytesPrefix(start)
|
||||
iRange.Start = key
|
||||
|
||||
iter := s.db.NewIterator(iRange, nil)
|
||||
for ok := iter.Last(); ok; ok = iter.Prev() {
|
||||
for ; ok; ok = next() {
|
||||
if !f(iter.Key(), iter.Value()) {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
// KeyPrefix constants.
|
||||
|
@ -133,6 +135,24 @@ func AppendPrefixInt(k KeyPrefix, n int) []byte {
|
|||
return AppendPrefix(k, b)
|
||||
}
|
||||
|
||||
func seekRangeToPrefixes(sr SeekRange) *util.Range {
|
||||
var (
|
||||
rang *util.Range
|
||||
start = make([]byte, len(sr.Prefix)+len(sr.Start))
|
||||
)
|
||||
copy(start, sr.Prefix)
|
||||
copy(start[len(sr.Prefix):], sr.Start)
|
||||
|
||||
if !sr.Backwards {
|
||||
rang = util.BytesPrefix(sr.Prefix)
|
||||
rang.Start = start
|
||||
} else {
|
||||
rang = util.BytesPrefix(start)
|
||||
rang.Start = sr.Prefix
|
||||
}
|
||||
return rang
|
||||
}
|
||||
|
||||
// NewStore creates storage with preselected in configuration database type.
|
||||
func NewStore(cfg DBConfiguration) (Store, error) {
|
||||
var store Store
|
||||
|
|
Loading…
Reference in a new issue