Skip to content

Commit

Permalink
leveldb: Implements slice support
Browse files Browse the repository at this point in the history
  • Loading branch information
syndtr committed Feb 24, 2014
1 parent 1d3a097 commit 26ca0eb
Show file tree
Hide file tree
Showing 22 changed files with 939 additions and 683 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Read or modify the database content:

Iterate over database content:

iter := db.NewIterator(nil)
iter := db.NewIterator(nil, nil)
for iter.Next() {
// Remember that the contents of the returned slice should not be modified, and
// only valid until the next call to Next.
Expand All @@ -44,6 +44,28 @@ Iterate over database content:
err = iter.Error()
...

Seek-then-Iterate:

iter := db.NewIterator(nil, nil)
for ok := iter.Seek(key); ok; ok = iter.Next() {
// Use key/value.
...
}
iter.Release()
err = iter.Error()
...

Iterate over subset of database content:

iter := db.NewIterator(&util.Range{Start: []byte("foo"), Limit: []byte("xoo")}, nil)
for iter.Next() {
// Use key/value.
...
}
iter.Release()
err = iter.Error()
...

Batch writes:

batch := new(leveldb.Batch)
Expand Down
2 changes: 1 addition & 1 deletion leveldb/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (p *dbBench) seeks() {
}

func (p *dbBench) newIter() iterator.Iterator {
iter := p.db.NewIterator(p.ro)
iter := p.db.NewIterator(nil, p.ro)
err := iter.Error()
if err != nil {
p.b.Fatal("cannot create iterator: ", err)
Expand Down
2 changes: 1 addition & 1 deletion leveldb/corrupt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (h *dbCorruptHarness) check(min, max int) {
db := p.db

var n, badk, badv, missed, good int
iter := db.NewIterator(p.ro)
iter := db.NewIterator(nil, p.ro)
for iter.Next() {
k := 0
fmt.Sscanf(string(iter.Key()), "%d", &k)
Expand Down
13 changes: 9 additions & 4 deletions leveldb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func Recover(p storage.Storage, o *opt.Options) (db *DB, err error) {
}

t := newTFile(f, uint64(size), nil, nil)
iter := s.tops.newIterator(t, nil)
iter := s.tops.newIterator(t, nil, nil)
// min ikey
if iter.First() {
t.min = iter.Key()
Expand Down Expand Up @@ -266,7 +266,7 @@ func Recover(p storage.Storage, o *opt.Options) (db *DB, err error) {
// extract largest seq number from newest table
if nt != nil {
var lseq uint64
iter := s.tops.newIterator(nt, nil)
iter := s.tops.newIterator(nt, nil, nil)
for iter.Next() {
seq, _, ok := iKey(iter.Key()).parseNum()
if !ok {
Expand Down Expand Up @@ -473,17 +473,22 @@ func (d *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
// underlying DB. The resultant key/value pairs are guaranteed to be
// consistent.
//
// Slice allows slicing the iterator to only contains keys in the given
// range. A nil Range.Start is treated as a key before all keys in the
// DB. And a nil Range.Limit is treated as a key after all keys in
// the DB.
//
// The iterator must be released after use, by calling Release method.
//
// Also read Iterator documentation of the leveldb/iterator package.
func (d *DB) NewIterator(ro *opt.ReadOptions) iterator.Iterator {
func (d *DB) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
if err := d.ok(); err != nil {
return iterator.NewEmptyIterator(err)
}

p := d.newSnapshot()
defer p.Release()
return p.NewIterator(ro)
return p.NewIterator(slice, ro)
}

// GetSnapshot returns a latest snapshot of the underlying DB. A snapshot
Expand Down
60 changes: 37 additions & 23 deletions leveldb/db_iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,44 @@ var (
errInvalidIkey = errors.New("leveldb: Iterator: invalid internal key")
)

func (db *DB) newRawIterator(ro *opt.ReadOptions) iterator.Iterator {
func (db *DB) newRawIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
s := db.s

em, fm := db.getMems()
v := s.version()

ti := v.getIterators(ro)
ti := v.getIterators(slice, ro)
n := len(ti) + 2
i := make([]iterator.Iterator, 0, n)
i = append(i, em.NewIterator(nil))
i = append(i, em.NewIterator(slice))
if fm != nil {
i = append(i, fm.NewIterator(nil))
i = append(i, fm.NewIterator(slice))
}
i = append(i, ti...)
mi := iterator.NewMergedIterator(i, s.cmp, true)
mi.SetReleaser(&versionReleaser{v: v})
return mi
}

func (db *DB) newIterator(seq uint64, ro *opt.ReadOptions) *dbIter {
rawIter := db.newRawIterator(ro)
func (db *DB) newIterator(seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter {
var slice_ *util.Range
if slice != nil {
slice_ = &util.Range{}
if slice.Start != nil {
slice_.Start = newIKey(slice.Start, kMaxSeq, tSeek)
}
if slice.Limit != nil {
slice_.Limit = newIKey(slice.Limit, kMaxSeq, tSeek)
}
}
rawIter := db.newRawIterator(slice_, ro)
iter := &dbIter{
cmp: db.s.cmp.cmp,
iter: rawIter,
seq: seq,
strict: db.s.o.GetStrict(opt.StrictIterator) || ro.GetStrict(opt.StrictIterator),
key: make([]byte, 0),
value: make([]byte, 0),
}
runtime.SetFinalizer(iter, (*dbIter).Release)
return iter
Expand Down Expand Up @@ -193,25 +205,27 @@ func (i *dbIter) Next() bool {
func (i *dbIter) prev() bool {
i.dir = dirBackward
del := true
for {
ukey, seq, t, ok := parseIkey(i.iter.Key())
if ok {
if seq <= i.seq {
if !del && i.cmp.Compare(ukey, i.key) < 0 {
return true
}
del = (t == tDel)
if !del {
i.key = append(i.key[:0], ukey...)
i.value = append(i.value[:0], i.iter.Value()...)
if i.iter.Valid() {
for {
ukey, seq, t, ok := parseIkey(i.iter.Key())
if ok {
if seq <= i.seq {
if !del && i.cmp.Compare(ukey, i.key) < 0 {
return true
}
del = (t == tDel)
if !del {
i.key = append(i.key[:0], ukey...)
i.value = append(i.value[:0], i.iter.Value()...)
}
}
} else if i.strict {
i.setErr(errInvalidIkey)
return false
}
if !i.iter.Prev() {
break
}
} else if i.strict {
i.setErr(errInvalidIkey)
return false
}
if !i.iter.Prev() {
break
}
}
if del {
Expand Down
10 changes: 8 additions & 2 deletions leveldb/db_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
)

type snapshotElement struct {
Expand Down Expand Up @@ -119,12 +120,17 @@ func (p *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error
// underlying DB. The resultant key/value pairs are guaranteed to be
// consistent.
//
// Slice allows slicing the iterator to only contains keys in the given
// range. A nil Range.Start is treated as a key before all keys in the
// DB. And a nil Range.Limit is treated as a key after all keys in
// the DB.
//
// The iterator must be released after use, by calling Release method.
// Releasing the snapshot doesn't mean releasing the iterator too, the
// iterator would be still valid until released.
//
// Also read Iterator documentation of the leveldb/iterator package.
func (p *Snapshot) NewIterator(ro *opt.ReadOptions) iterator.Iterator {
func (p *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
db := p.db
if err := db.ok(); err != nil {
return iterator.NewEmptyIterator(err)
Expand All @@ -134,7 +140,7 @@ func (p *Snapshot) NewIterator(ro *opt.ReadOptions) iterator.Iterator {
if p.released {
return iterator.NewEmptyIterator(ErrSnapshotReleased)
}
return db.newIterator(p.elem.seq, ro)
return db.newIterator(p.elem.seq, slice, ro)
}

// Release releases the snapshot. This will not release any returned
Expand Down
20 changes: 10 additions & 10 deletions leveldb/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (h *dbHarness) allEntriesFor(key, want string) {
ucmp := db.s.cmp.cmp

ikey := newIKey([]byte(key), kMaxSeq, tVal)
iter := db.newRawIterator(new(opt.ReadOptions))
iter := db.newRawIterator(nil, nil)
if !iter.Seek(ikey) && iter.Error() != nil {
t.Error("AllEntries: error during seek, err: ", iter.Error())
return
Expand Down Expand Up @@ -276,7 +276,7 @@ func (h *dbHarness) getKeyVal(want string) {
t.Fatal("GetSnapshot: got error: ", err)
}
res := ""
iter := s.NewIterator(new(opt.ReadOptions))
iter := s.NewIterator(nil, nil)
for iter.Next() {
res += fmt.Sprintf("(%s->%s)", string(iter.Key()), string(iter.Value()))
}
Expand Down Expand Up @@ -766,7 +766,7 @@ func TestDb_IterMultiWithDelete(t *testing.T) {
h.delete("b")
h.get("b", false)

iter := h.db.NewIterator(new(opt.ReadOptions))
iter := h.db.NewIterator(nil, nil)
iter.Seek([]byte("c"))
testKeyVal(t, iter, "c->vc")
iter.Prev()
Expand All @@ -775,7 +775,7 @@ func TestDb_IterMultiWithDelete(t *testing.T) {

h.compactMem()

iter = h.db.NewIterator(new(opt.ReadOptions))
iter = h.db.NewIterator(nil, nil)
iter.Seek([]byte("c"))
testKeyVal(t, iter, "c->vc")
iter.Prev()
Expand All @@ -791,7 +791,7 @@ func TestDb_IteratorPinsRef(t *testing.T) {
h.put("foo", "hello")

// Get iterator that will yield the current contents of the DB.
iter := h.db.NewIterator(new(opt.ReadOptions))
iter := h.db.NewIterator(nil, nil)

// Write to force compactions
h.put("foo", "newvalue1")
Expand Down Expand Up @@ -1424,7 +1424,7 @@ func TestDb_ClosedIsClosed(t *testing.T) {
h.put("k", "v")
h.getVal("k", "v")

iter = db.NewIterator(h.ro)
iter = db.NewIterator(nil, h.ro)
iter.Seek([]byte("k"))
testKeyVal(t, iter, "k->v")

Expand All @@ -1436,7 +1436,7 @@ func TestDb_ClosedIsClosed(t *testing.T) {

h.getValr(snap, "k", "v")

iter2 = snap.NewIterator(h.ro)
iter2 = snap.NewIterator(nil, h.ro)
iter2.Seek([]byte("k"))
testKeyVal(t, iter2, "k->v")

Expand Down Expand Up @@ -1470,10 +1470,10 @@ func TestDb_ClosedIsClosed(t *testing.T) {
_, err = db.GetSnapshot()
assertErr(t, err, true)

iter3 := db.NewIterator(h.ro)
iter3 := db.NewIterator(nil, h.ro)
assertErr(t, iter3.Error(), true)

iter3 = snap.NewIterator(h.ro)
iter3 = snap.NewIterator(nil, h.ro)
assertErr(t, iter3.Error(), true)

assertErr(t, db.Delete([]byte("k"), h.wo), true)
Expand Down Expand Up @@ -1725,7 +1725,7 @@ func TestDb_Concurrent2(t *testing.T) {
for i := 0; i < n2; i++ {
closeWg.Add(1)
go func(i int) {
it := h.db.NewIterator(nil)
it := h.db.NewIterator(nil, nil)
var pk []byte
for it.Next() {
kk := it.Key()
Expand Down
3 changes: 2 additions & 1 deletion leveldb/db_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import (
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
)

// Reader is the interface that wraps basic Get and NewIterator methods.
// This interface implemented by both DB and Snapshot.
type Reader interface {
Get(key []byte, ro *opt.ReadOptions) (value []byte, err error)
NewIterator(ro *opt.ReadOptions) iterator.Iterator
NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator
}

type Sizes []uint64
Expand Down
24 changes: 23 additions & 1 deletion leveldb/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//
// Iterate over database content:
//
// iter := db.NewIterator(nil)
// iter := db.NewIterator(nil, nil)
// for iter.Next() {
// // Remember that the contents of the returned slice should not be modified, and
// // only valid until the next call to Next.
Expand All @@ -37,6 +37,28 @@
// err = iter.Error()
// ...
//
// Seek-then-Iterate:
//
// iter := db.NewIterator(nil, nil)
// for ok := iter.Seek(key); ok; ok = iter.Next() {
// // Use key/value.
// ...
// }
// iter.Release()
// err = iter.Error()
// ...
//
// Iterate over subset of database content:
//
// iter := db.NewIterator(&util.Range{Start: []byte("foo"), Limit: []byte("xoo")}, nil)
// for iter.Next() {
// // Use key/value.
// ...
// }
// iter.Release()
// err = iter.Error()
// ...
//
// Batch writes:
//
// batch := new(leveldb.Batch)
Expand Down
Loading

0 comments on commit 26ca0eb

Please sign in to comment.