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