You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.8 KiB
139 lines
3.8 KiB
6 years ago
|
// Copyright (c) 2018 Couchbase, Inc.
|
||
|
//
|
||
|
// 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 zap
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
|
||
|
"github.com/couchbase/vellum"
|
||
|
)
|
||
|
|
||
|
// enumerator provides an ordered traversal of multiple vellum
|
||
|
// iterators. Like JOIN of iterators, the enumerator produces a
|
||
|
// sequence of (key, iteratorIndex, value) tuples, sorted by key ASC,
|
||
|
// then iteratorIndex ASC, where the same key might be seen or
|
||
|
// repeated across multiple child iterators.
|
||
|
type enumerator struct {
|
||
|
itrs []vellum.Iterator
|
||
|
currKs [][]byte
|
||
|
currVs []uint64
|
||
|
|
||
|
lowK []byte
|
||
|
lowIdxs []int
|
||
|
lowCurr int
|
||
|
}
|
||
|
|
||
|
// newEnumerator returns a new enumerator over the vellum Iterators
|
||
|
func newEnumerator(itrs []vellum.Iterator) (*enumerator, error) {
|
||
|
rv := &enumerator{
|
||
|
itrs: itrs,
|
||
|
currKs: make([][]byte, len(itrs)),
|
||
|
currVs: make([]uint64, len(itrs)),
|
||
|
lowIdxs: make([]int, 0, len(itrs)),
|
||
|
}
|
||
|
for i, itr := range rv.itrs {
|
||
|
rv.currKs[i], rv.currVs[i] = itr.Current()
|
||
|
}
|
||
6 years ago
|
rv.updateMatches(false)
|
||
|
if rv.lowK == nil && len(rv.lowIdxs) == 0 {
|
||
6 years ago
|
return rv, vellum.ErrIteratorDone
|
||
|
}
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// updateMatches maintains the low key matches based on the currKs
|
||
6 years ago
|
func (m *enumerator) updateMatches(skipEmptyKey bool) {
|
||
6 years ago
|
m.lowK = nil
|
||
|
m.lowIdxs = m.lowIdxs[:0]
|
||
|
m.lowCurr = 0
|
||
|
|
||
|
for i, key := range m.currKs {
|
||
6 years ago
|
if (key == nil && m.currVs[i] == 0) || // in case of empty iterator
|
||
|
(len(key) == 0 && skipEmptyKey) { // skip empty keys
|
||
6 years ago
|
continue
|
||
|
}
|
||
|
|
||
|
cmp := bytes.Compare(key, m.lowK)
|
||
6 years ago
|
if cmp < 0 || len(m.lowIdxs) == 0 {
|
||
6 years ago
|
// reached a new low
|
||
|
m.lowK = key
|
||
|
m.lowIdxs = m.lowIdxs[:0]
|
||
|
m.lowIdxs = append(m.lowIdxs, i)
|
||
|
} else if cmp == 0 {
|
||
|
m.lowIdxs = append(m.lowIdxs, i)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Current returns the enumerator's current key, iterator-index, and
|
||
|
// value. If the enumerator is not pointing at a valid value (because
|
||
|
// Next returned an error previously), Current will return nil,0,0.
|
||
|
func (m *enumerator) Current() ([]byte, int, uint64) {
|
||
|
var i int
|
||
|
var v uint64
|
||
|
if m.lowCurr < len(m.lowIdxs) {
|
||
|
i = m.lowIdxs[m.lowCurr]
|
||
|
v = m.currVs[i]
|
||
|
}
|
||
|
return m.lowK, i, v
|
||
|
}
|
||
|
|
||
5 years ago
|
// GetLowIdxsAndValues will return all of the iterator indices
|
||
|
// which point to the current key, and their corresponding
|
||
|
// values. This can be used by advanced caller which may need
|
||
|
// to peek into these other sets of data before processing.
|
||
|
func (m *enumerator) GetLowIdxsAndValues() ([]int, []uint64) {
|
||
|
values := make([]uint64, 0, len(m.lowIdxs))
|
||
|
for _, idx := range m.lowIdxs {
|
||
|
values = append(values, m.currVs[idx])
|
||
|
}
|
||
|
return m.lowIdxs, values
|
||
|
}
|
||
|
|
||
6 years ago
|
// Next advances the enumerator to the next key/iterator/value result,
|
||
|
// else vellum.ErrIteratorDone is returned.
|
||
|
func (m *enumerator) Next() error {
|
||
|
m.lowCurr += 1
|
||
|
if m.lowCurr >= len(m.lowIdxs) {
|
||
|
// move all the current low iterators forwards
|
||
|
for _, vi := range m.lowIdxs {
|
||
|
err := m.itrs[vi].Next()
|
||
|
if err != nil && err != vellum.ErrIteratorDone {
|
||
|
return err
|
||
|
}
|
||
|
m.currKs[vi], m.currVs[vi] = m.itrs[vi].Current()
|
||
|
}
|
||
6 years ago
|
// can skip any empty keys encountered at this point
|
||
|
m.updateMatches(true)
|
||
6 years ago
|
}
|
||
6 years ago
|
if m.lowK == nil && len(m.lowIdxs) == 0 {
|
||
6 years ago
|
return vellum.ErrIteratorDone
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Close all the underlying Iterators. The first error, if any, will
|
||
|
// be returned.
|
||
|
func (m *enumerator) Close() error {
|
||
|
var rv error
|
||
|
for _, itr := range m.itrs {
|
||
|
err := itr.Close()
|
||
|
if rv == nil {
|
||
|
rv = err
|
||
|
}
|
||
|
}
|
||
|
return rv
|
||
|
}
|