storage: generalize Level/Bolt seek implementations

Too much in common. Just refactoring. no functional changes.
This commit is contained in:
Roman Khimov 2022-02-11 18:00:45 +03:00
parent a5f8b8870a
commit 51b804ab0e
3 changed files with 51 additions and 60 deletions

View file

@ -7,7 +7,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/syndtr/goleveldb/leveldb/util"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
@ -110,44 +109,30 @@ func (s *BoltDBStore) PutChangeSet(puts map[string][]byte) error {
// Seek implements the Store interface. // Seek implements the Store interface.
func (s *BoltDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) { func (s *BoltDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
start := make([]byte, len(rng.Prefix)+len(rng.Start)) rang := seekRangeToPrefixes(rng)
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
err := s.db.View(func(tx *bbolt.Tx) error { err := s.db.View(func(tx *bbolt.Tx) error {
var (
k, v []byte
next func() ([]byte, []byte)
)
c := tx.Bucket(Bucket).Cursor() 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) { if !rng.Backwards {
break 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) { for ; k != nil && bytes.HasPrefix(k, rng.Prefix) && (len(rang.Limit) == 0 || bytes.Compare(k, rang.Limit) <= 0); k, v = next() {
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() {
if !f(k, v) { if !f(k, v) {
break break
} }

View file

@ -4,7 +4,6 @@ import (
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
) )
// LevelDBOptions configuration for LevelDB. // LevelDBOptions configuration for LevelDB.
@ -83,34 +82,21 @@ func (s *LevelDBStore) PutChangeSet(puts map[string][]byte) error {
// Seek implements the Store interface. // Seek implements the Store interface.
func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) { func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
start := make([]byte, len(rng.Prefix)+len(rng.Start)) var (
copy(start, rng.Prefix) next func() bool
copy(start[len(rng.Prefix):], rng.Start) ok bool
if rng.Backwards { iter = s.db.NewIterator(seekRangeToPrefixes(rng), nil)
s.seekBackwards(rng.Prefix, start, f) )
if !rng.Backwards {
ok = iter.Next()
next = iter.Next
} else { } 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) { for ; ok; ok = next() {
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() {
if !f(iter.Key(), iter.Value()) { if !f(iter.Key(), iter.Value()) {
break break
} }

View file

@ -4,6 +4,8 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/syndtr/goleveldb/leveldb/util"
) )
// KeyPrefix constants. // KeyPrefix constants.
@ -133,6 +135,24 @@ func AppendPrefixInt(k KeyPrefix, n int) []byte {
return AppendPrefix(k, b) 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. // NewStore creates storage with preselected in configuration database type.
func NewStore(cfg DBConfiguration) (Store, error) { func NewStore(cfg DBConfiguration) (Store, error) {
var store Store var store Store