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/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
}

View file

@ -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
}

View file

@ -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