229 lines
5 KiB
Go
229 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)
|
|
}
|