syncthing/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_iter.go

325 lines
5.8 KiB
Go
Raw Normal View History

2014-07-06 14:46:48 +02:00
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package leveldb
import (
"errors"
"runtime"
2014-08-15 09:16:30 +02:00
"sync"
2014-07-06 14:46:48 +02:00
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
)
var (
errInvalidIkey = errors.New("leveldb: Iterator: invalid internal key")
)
2014-08-15 09:16:30 +02:00
type memdbReleaser struct {
once sync.Once
m *memDB
}
func (mr *memdbReleaser) Release() {
mr.once.Do(func() {
mr.m.decref()
})
}
2014-07-06 14:46:48 +02:00
func (db *DB) newRawIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
em, fm := db.getMems()
2014-07-23 08:31:36 +02:00
v := db.s.version()
2014-07-06 14:46:48 +02:00
ti := v.getIterators(slice, ro)
n := len(ti) + 2
i := make([]iterator.Iterator, 0, n)
2014-08-15 09:16:30 +02:00
emi := em.db.NewIterator(slice)
emi.SetReleaser(&memdbReleaser{m: em})
i = append(i, emi)
2014-07-06 14:46:48 +02:00
if fm != nil {
2014-08-15 09:16:30 +02:00
fmi := fm.db.NewIterator(slice)
fmi.SetReleaser(&memdbReleaser{m: fm})
i = append(i, fmi)
2014-07-06 14:46:48 +02:00
}
i = append(i, ti...)
2014-07-23 08:31:36 +02:00
strict := db.s.o.GetStrict(opt.StrictIterator) || ro.GetStrict(opt.StrictIterator)
mi := iterator.NewMergedIterator(i, db.s.icmp, strict)
2014-07-06 14:46:48 +02:00
mi.SetReleaser(&versionReleaser{v: v})
return mi
}
func (db *DB) newIterator(seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter {
2014-07-23 08:31:36 +02:00
var islice *util.Range
2014-07-06 14:46:48 +02:00
if slice != nil {
2014-07-23 08:31:36 +02:00
islice = &util.Range{}
2014-07-06 14:46:48 +02:00
if slice.Start != nil {
2014-07-23 08:31:36 +02:00
islice.Start = newIKey(slice.Start, kMaxSeq, tSeek)
2014-07-06 14:46:48 +02:00
}
if slice.Limit != nil {
2014-07-23 08:31:36 +02:00
islice.Limit = newIKey(slice.Limit, kMaxSeq, tSeek)
2014-07-06 14:46:48 +02:00
}
}
2014-07-23 08:31:36 +02:00
rawIter := db.newRawIterator(islice, ro)
2014-07-06 14:46:48 +02:00
iter := &dbIter{
2014-07-06 23:13:10 +02:00
icmp: db.s.icmp,
2014-07-06 14:46:48 +02:00
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
}
type dir int
const (
dirReleased dir = iota - 1
dirSOI
dirEOI
dirBackward
dirForward
)
// dbIter represent an interator states over a database session.
type dbIter struct {
2014-07-06 23:13:10 +02:00
icmp *iComparer
2014-07-06 14:46:48 +02:00
iter iterator.Iterator
seq uint64
strict bool
dir dir
key []byte
value []byte
err error
releaser util.Releaser
}
func (i *dbIter) setErr(err error) {
i.err = err
i.key = nil
i.value = nil
}
func (i *dbIter) iterErr() {
if err := i.iter.Error(); err != nil {
i.setErr(err)
}
}
func (i *dbIter) Valid() bool {
return i.err == nil && i.dir > dirEOI
}
func (i *dbIter) First() bool {
if i.err != nil {
return false
} else if i.dir == dirReleased {
i.err = ErrIterReleased
return false
}
if i.iter.First() {
i.dir = dirSOI
return i.next()
}
i.dir = dirEOI
i.iterErr()
return false
}
func (i *dbIter) Last() bool {
if i.err != nil {
return false
} else if i.dir == dirReleased {
i.err = ErrIterReleased
return false
}
if i.iter.Last() {
return i.prev()
}
i.dir = dirSOI
i.iterErr()
return false
}
func (i *dbIter) Seek(key []byte) bool {
if i.err != nil {
return false
} else if i.dir == dirReleased {
i.err = ErrIterReleased
return false
}
ikey := newIKey(key, i.seq, tSeek)
if i.iter.Seek(ikey) {
i.dir = dirSOI
return i.next()
}
i.dir = dirEOI
i.iterErr()
return false
}
func (i *dbIter) next() bool {
for {
ukey, seq, t, ok := parseIkey(i.iter.Key())
if ok {
if seq <= i.seq {
switch t {
case tDel:
// Skip deleted key.
i.key = append(i.key[:0], ukey...)
i.dir = dirForward
case tVal:
2014-07-06 23:13:10 +02:00
if i.dir == dirSOI || i.icmp.uCompare(ukey, i.key) > 0 {
2014-07-06 14:46:48 +02:00
i.key = append(i.key[:0], ukey...)
i.value = append(i.value[:0], i.iter.Value()...)
i.dir = dirForward
return true
}
}
}
} else if i.strict {
i.setErr(errInvalidIkey)
break
}
if !i.iter.Next() {
i.dir = dirEOI
i.iterErr()
break
}
}
return false
}
func (i *dbIter) Next() bool {
if i.dir == dirEOI || i.err != nil {
return false
} else if i.dir == dirReleased {
i.err = ErrIterReleased
return false
}
if !i.iter.Next() || (i.dir == dirBackward && !i.iter.Next()) {
i.dir = dirEOI
i.iterErr()
return false
}
return i.next()
}
func (i *dbIter) prev() bool {
i.dir = dirBackward
del := true
if i.iter.Valid() {
for {
ukey, seq, t, ok := parseIkey(i.iter.Key())
if ok {
if seq <= i.seq {
2014-07-06 23:13:10 +02:00
if !del && i.icmp.uCompare(ukey, i.key) < 0 {
2014-07-06 14:46:48 +02:00
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
}
}
}
if del {
i.dir = dirSOI
i.iterErr()
return false
}
return true
}
func (i *dbIter) Prev() bool {
if i.dir == dirSOI || i.err != nil {
return false
} else if i.dir == dirReleased {
i.err = ErrIterReleased
return false
}
switch i.dir {
case dirEOI:
return i.Last()
case dirForward:
for i.iter.Prev() {
ukey, _, _, ok := parseIkey(i.iter.Key())
if ok {
2014-07-06 23:13:10 +02:00
if i.icmp.uCompare(ukey, i.key) < 0 {
2014-07-06 14:46:48 +02:00
goto cont
}
} else if i.strict {
i.setErr(errInvalidIkey)
return false
}
}
i.dir = dirSOI
i.iterErr()
return false
}
cont:
return i.prev()
}
func (i *dbIter) Key() []byte {
if i.err != nil || i.dir <= dirEOI {
return nil
}
return i.key
}
func (i *dbIter) Value() []byte {
if i.err != nil || i.dir <= dirEOI {
return nil
}
return i.value
}
func (i *dbIter) Release() {
if i.dir != dirReleased {
// Clear the finalizer.
runtime.SetFinalizer(i, nil)
if i.releaser != nil {
i.releaser.Release()
}
i.dir = dirReleased
i.key = nil
i.value = nil
i.iter.Release()
i.iter = nil
}
}
func (i *dbIter) SetReleaser(releaser util.Releaser) {
if i.dir != dirReleased {
i.releaser = releaser
}
}
func (i *dbIter) Error() error {
return i.err
}