meditime/vendor/github.com/dgraph-io/badger/table/merge_iterator.go

230 lines
5 KiB
Go

/*
* Copyright 2019 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package table
import (
"bytes"
"github.com/dgraph-io/badger/y"
"github.com/pkg/errors"
)
// MergeIterator merges multiple iterators.
// NOTE: MergeIterator owns the array of iterators and is responsible for closing them.
type MergeIterator struct {
left node
right node
small *node
curKey []byte
reverse bool
}
type node struct {
valid bool
key []byte
iter y.Iterator
// The two iterators are type asserted from `y.Iterator`, used to inline more function calls.
// Calling functions on concrete types is much faster (about 25-30%) than calling the
// interface's function.
merge *MergeIterator
concat *ConcatIterator
}
func (n *node) setIterator(iter y.Iterator) {
n.iter = iter
// It's okay if the type assertion below fails and n.merge/n.concat are set to nil.
// We handle the nil values of merge and concat in all the methods.
n.merge, _ = iter.(*MergeIterator)
n.concat, _ = iter.(*ConcatIterator)
}
func (n *node) setKey() {
if n.merge != nil {
n.valid = n.merge.small.valid
if n.valid {
n.key = n.merge.small.key
}
} else if n.concat != nil {
n.valid = n.concat.Valid()
if n.valid {
n.key = n.concat.Key()
}
} else {
n.valid = n.iter.Valid()
if n.valid {
n.key = n.iter.Key()
}
}
}
func (n *node) next() {
if n.merge != nil {
n.merge.Next()
} else if n.concat != nil {
n.concat.Next()
} else {
n.iter.Next()
}
n.setKey()
}
func (n *node) rewind() {
n.iter.Rewind()
n.setKey()
}
func (n *node) seek(key []byte) {
n.iter.Seek(key)
n.setKey()
}
func (mi *MergeIterator) fix() {
if !mi.bigger().valid {
return
}
if !mi.small.valid {
mi.swapSmall()
return
}
cmp := y.CompareKeys(mi.small.key, mi.bigger().key)
// Both the keys are equal.
if cmp == 0 {
// In case of same keys, move the right iterator ahead.
mi.right.next()
if &mi.right == mi.small {
mi.swapSmall()
}
return
} else if cmp < 0 { // Small is less than bigger().
if mi.reverse {
mi.swapSmall()
} else {
// we don't need to do anything. Small already points to the smallest.
}
return
} else { // bigger() is less than small.
if mi.reverse {
// Do nothing since we're iterating in reverse. Small currently points to
// the bigger key and that's okay in reverse iteration.
} else {
mi.swapSmall()
}
return
}
}
func (mi *MergeIterator) bigger() *node {
if mi.small == &mi.left {
return &mi.right
}
return &mi.left
}
func (mi *MergeIterator) swapSmall() {
if mi.small == &mi.left {
mi.small = &mi.right
return
}
if mi.small == &mi.right {
mi.small = &mi.left
return
}
}
// Next returns the next element. If it is the same as the current key, ignore it.
func (mi *MergeIterator) Next() {
for mi.Valid() {
if !bytes.Equal(mi.small.key, mi.curKey) {
break
}
mi.small.next()
mi.fix()
}
mi.setCurrent()
}
func (mi *MergeIterator) setCurrent() {
mi.curKey = append(mi.curKey[:0], mi.small.key...)
}
// Rewind seeks to first element (or last element for reverse iterator).
func (mi *MergeIterator) Rewind() {
mi.left.rewind()
mi.right.rewind()
mi.fix()
mi.setCurrent()
}
// Seek brings us to element with key >= given key.
func (mi *MergeIterator) Seek(key []byte) {
mi.left.seek(key)
mi.right.seek(key)
mi.fix()
mi.setCurrent()
}
// Valid returns whether the MergeIterator is at a valid element.
func (mi *MergeIterator) Valid() bool {
return mi.small.valid
}
// Key returns the key associated with the current iterator.
func (mi *MergeIterator) Key() []byte {
return mi.small.key
}
// Value returns the value associated with the iterator.
func (mi *MergeIterator) Value() y.ValueStruct {
return mi.small.iter.Value()
}
// Close implements y.Iterator.
func (mi *MergeIterator) Close() error {
err1 := mi.left.iter.Close()
err2 := mi.right.iter.Close()
if err1 != nil {
return errors.Wrap(err1, "MergeIterator")
}
return errors.Wrap(err2, "MergeIterator")
}
// NewMergeIterator creates a merge iterator.
func NewMergeIterator(iters []y.Iterator, reverse bool) y.Iterator {
if len(iters) == 0 {
return nil
} else if len(iters) == 1 {
return iters[0]
} else if len(iters) == 2 {
mi := &MergeIterator{
reverse: reverse,
}
mi.left.setIterator(iters[0])
mi.right.setIterator(iters[1])
// Assign left iterator randomly. This will be fixed when user calls rewind/seek.
mi.small = &mi.left
return mi
}
mid := len(iters) / 2
return NewMergeIterator(
[]y.Iterator{
NewMergeIterator(iters[:mid], reverse),
NewMergeIterator(iters[mid:], reverse),
}, reverse)
}