|
- // 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"
- "math/rand"
- "runtime"
- "sync"
- "sync/atomic"
-
- "github.com/syndtr/goleveldb/leveldb/iterator"
- "github.com/syndtr/goleveldb/leveldb/opt"
- "github.com/syndtr/goleveldb/leveldb/util"
- )
-
- var (
- errInvalidInternalKey = errors.New("leveldb: Iterator: invalid internal key")
- )
-
- type memdbReleaser struct {
- once sync.Once
- m *memDB
- }
-
- func (mr *memdbReleaser) Release() {
- mr.once.Do(func() {
- mr.m.decref()
- })
- }
-
- func (db *DB) newRawIterator(auxm *memDB, auxt tFiles, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
- strict := opt.GetStrict(db.s.o.Options, ro, opt.StrictReader)
- em, fm := db.getMems()
- v := db.s.version()
-
- tableIts := v.getIterators(slice, ro)
- n := len(tableIts) + len(auxt) + 3
- its := make([]iterator.Iterator, 0, n)
-
- if auxm != nil {
- ami := auxm.NewIterator(slice)
- ami.SetReleaser(&memdbReleaser{m: auxm})
- its = append(its, ami)
- }
- for _, t := range auxt {
- its = append(its, v.s.tops.newIterator(t, slice, ro))
- }
-
- emi := em.NewIterator(slice)
- emi.SetReleaser(&memdbReleaser{m: em})
- its = append(its, emi)
- if fm != nil {
- fmi := fm.NewIterator(slice)
- fmi.SetReleaser(&memdbReleaser{m: fm})
- its = append(its, fmi)
- }
- its = append(its, tableIts...)
- mi := iterator.NewMergedIterator(its, db.s.icmp, strict)
- mi.SetReleaser(&versionReleaser{v: v})
- return mi
- }
-
- func (db *DB) newIterator(auxm *memDB, auxt tFiles, seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter {
- var islice *util.Range
- if slice != nil {
- islice = &util.Range{}
- if slice.Start != nil {
- islice.Start = makeInternalKey(nil, slice.Start, keyMaxSeq, keyTypeSeek)
- }
- if slice.Limit != nil {
- islice.Limit = makeInternalKey(nil, slice.Limit, keyMaxSeq, keyTypeSeek)
- }
- }
- rawIter := db.newRawIterator(auxm, auxt, islice, ro)
- iter := &dbIter{
- db: db,
- icmp: db.s.icmp,
- iter: rawIter,
- seq: seq,
- strict: opt.GetStrict(db.s.o.Options, ro, opt.StrictReader),
- key: make([]byte, 0),
- value: make([]byte, 0),
- }
- atomic.AddInt32(&db.aliveIters, 1)
- runtime.SetFinalizer(iter, (*dbIter).Release)
- return iter
- }
-
- func (db *DB) iterSamplingRate() int {
- return rand.Intn(2 * db.s.o.GetIteratorSamplingRate())
- }
-
- type dir int
-
- const (
- dirReleased dir = iota - 1
- dirSOI
- dirEOI
- dirBackward
- dirForward
- )
-
- // dbIter represent an interator states over a database session.
- type dbIter struct {
- db *DB
- icmp *iComparer
- iter iterator.Iterator
- seq uint64
- strict bool
-
- smaplingGap int
- dir dir
- key []byte
- value []byte
- err error
- releaser util.Releaser
- }
-
- func (i *dbIter) sampleSeek() {
- ikey := i.iter.Key()
- i.smaplingGap -= len(ikey) + len(i.iter.Value())
- for i.smaplingGap < 0 {
- i.smaplingGap += i.db.iterSamplingRate()
- i.db.sampleSeek(ikey)
- }
- }
-
- 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 := makeInternalKey(nil, key, i.seq, keyTypeSeek)
- if i.iter.Seek(ikey) {
- i.dir = dirSOI
- return i.next()
- }
- i.dir = dirEOI
- i.iterErr()
- return false
- }
-
- func (i *dbIter) next() bool {
- for {
- if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
- i.sampleSeek()
- if seq <= i.seq {
- switch kt {
- case keyTypeDel:
- // Skip deleted key.
- i.key = append(i.key[:0], ukey...)
- i.dir = dirForward
- case keyTypeVal:
- if i.dir == dirSOI || i.icmp.uCompare(ukey, i.key) > 0 {
- 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(kerr)
- 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 {
- if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
- i.sampleSeek()
- if seq <= i.seq {
- if !del && i.icmp.uCompare(ukey, i.key) < 0 {
- return true
- }
- del = (kt == keyTypeDel)
- if !del {
- i.key = append(i.key[:0], ukey...)
- i.value = append(i.value[:0], i.iter.Value()...)
- }
- }
- } else if i.strict {
- i.setErr(kerr)
- 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() {
- if ukey, _, _, kerr := parseInternalKey(i.iter.Key()); kerr == nil {
- i.sampleSeek()
- if i.icmp.uCompare(ukey, i.key) < 0 {
- goto cont
- }
- } else if i.strict {
- i.setErr(kerr)
- 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.releaser = nil
- }
-
- i.dir = dirReleased
- i.key = nil
- i.value = nil
- i.iter.Release()
- i.iter = nil
- atomic.AddInt32(&i.db.aliveIters, -1)
- i.db = nil
- }
- }
-
- func (i *dbIter) SetReleaser(releaser util.Releaser) {
- if i.dir == dirReleased {
- panic(util.ErrReleased)
- }
- if i.releaser != nil && releaser != nil {
- panic(util.ErrHasReleaser)
- }
- i.releaser = releaser
- }
-
- func (i *dbIter) Error() error {
- return i.err
- }
|