blob: 4dd02aaf7da0fdd5734efc877a9a257b70ac1457 [file] [log] [blame]
/*
* Copyright 2017 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"
"io"
"math"
"sort"
"github.com/dgraph-io/badger/y"
)
type BlockIterator struct {
data []byte
pos int
err error
baseKey []byte
ikey []byte
key []byte
val []byte
init bool
last header // The last header we saw.
}
func (itr *BlockIterator) Reset() {
itr.pos = 0
itr.err = nil
itr.baseKey = []byte{}
itr.key = []byte{}
itr.val = []byte{}
itr.init = false
itr.last = header{}
}
func (itr *BlockIterator) Init() {
if !itr.init {
itr.Next()
}
}
func (itr *BlockIterator) Valid() bool {
return itr != nil && itr.err == nil
}
func (itr *BlockIterator) Error() error {
return itr.err
}
func (itr *BlockIterator) ensureKeyCap(h header) {
if cap(itr.ikey) < h.plen+h.klen {
sz := h.plen + h.klen
if sz < 2*cap(itr.ikey) {
sz = 2 * cap(itr.ikey)
}
itr.ikey = make([]byte, sz)
}
}
func (itr *BlockIterator) Close() {}
var (
ORIGIN = 0
CURRENT = 1
)
// Seek brings us to the first block element that is >= input key.
func (itr *BlockIterator) Seek(key []byte, whence int) {
itr.err = nil
switch whence {
case ORIGIN:
itr.Reset()
case CURRENT:
}
var done bool
for itr.Init(); itr.Valid(); itr.Next() {
k := itr.Key()
if bytes.Compare(k, key) >= 0 {
// We are done as k is >= key.
done = true
break
}
}
if !done {
itr.err = io.EOF
}
}
func (itr *BlockIterator) SeekToFirst() {
itr.err = nil
itr.Init()
}
// SeekToLast brings us to the last element. Valid should return true.
func (itr *BlockIterator) SeekToLast() {
itr.err = nil
for itr.Init(); itr.Valid(); itr.Next() {
}
itr.Prev()
}
// parseKV would allocate a new byte slice for key and for value.
func (itr *BlockIterator) parseKV(h header) {
// itr.ensureKeyCap(h)
itr.key = make([]byte, h.plen+h.klen)
// itr.key = itr.ikey[:h.plen+h.klen]
y.AssertTrue(h.plen == copy(itr.key, itr.baseKey[:h.plen]))
y.AssertTrue(h.klen == copy(itr.key[h.plen:], itr.data[itr.pos:itr.pos+h.klen]))
itr.pos += h.klen
if itr.pos+h.vlen > len(itr.data) {
itr.err = y.Errorf("Value exceeded size of block: %d %d %d %d %v", itr.pos, h.klen, h.vlen, len(itr.data), h)
return
}
itr.val = make([]byte, h.vlen)
y.AssertTrue(h.vlen == copy(itr.val, itr.data[itr.pos:itr.pos+h.vlen]))
itr.val = itr.data[itr.pos : itr.pos+h.vlen]
itr.pos += h.vlen
}
func (itr *BlockIterator) Next() {
itr.init = true
itr.err = nil
if itr.pos >= len(itr.data) {
itr.err = io.EOF
return
}
var h header
itr.pos += h.Decode(itr.data[itr.pos:])
itr.last = h // Store the last header.
if h.klen == 0 && h.plen == 0 {
// Last entry in the table.
itr.err = io.EOF
return
}
// Populate baseKey if it isn't set yet. This would only happen for the first Next.
if len(itr.baseKey) == 0 {
// This should be the first Next() for this block. Hence, prefix length should be zero.
y.AssertTrue(h.plen == 0)
itr.baseKey = itr.data[itr.pos : itr.pos+h.klen]
}
itr.parseKV(h)
}
func (itr *BlockIterator) Prev() {
if !itr.init {
return
}
itr.err = nil
if itr.last.prev == math.MaxUint32 {
// This is the first element of the block!
itr.err = io.EOF
itr.pos = 0
return
}
// Move back using current header's prev.
itr.pos = itr.last.prev
var h header
y.AssertTruef(itr.pos >= 0 && itr.pos < len(itr.data), "%d %d", itr.pos, len(itr.data))
itr.pos += h.Decode(itr.data[itr.pos:])
itr.parseKV(h)
itr.last = h
}
func (itr *BlockIterator) Key() []byte {
if itr.err != nil {
return nil
}
return itr.key
}
func (itr *BlockIterator) Value() []byte {
if itr.err != nil {
return nil
}
return itr.val
}
type TableIterator struct {
t *Table
bpos int
bi *BlockIterator
err error
init bool
}
func (t *Table) NewIterator() *TableIterator {
t.IncrRef() // Important.
return &TableIterator{t: t}
}
func (itr *TableIterator) Close() {
itr.t.DecrRef()
}
func (itr *TableIterator) Reset() {
itr.bpos = 0
itr.err = nil
}
func (itr *TableIterator) Valid() bool {
return itr != nil && itr.err == nil
}
func (itr *TableIterator) Error() error {
return itr.err
}
func (itr *TableIterator) Init() {
if !itr.init {
itr.Next()
}
}
func (itr *TableIterator) SeekToFirst() {
numBlocks := len(itr.t.blockIndex)
if numBlocks == 0 {
itr.err = io.EOF
return
}
itr.bpos = 0
block, err := itr.t.block(itr.bpos)
if err != nil {
itr.err = err
return
}
itr.bi = block.NewIterator()
itr.bi.SeekToFirst()
itr.err = itr.bi.Error()
}
func (itr *TableIterator) SeekToLast() {
numBlocks := len(itr.t.blockIndex)
if numBlocks == 0 {
itr.err = io.EOF
return
}
itr.bpos = numBlocks - 1
block, err := itr.t.block(itr.bpos)
if err != nil {
itr.err = err
return
}
itr.bi = block.NewIterator()
itr.bi.SeekToLast()
itr.err = itr.bi.Error()
}
func (itr *TableIterator) seekHelper(blockIdx int, key []byte) {
y.AssertTrue(blockIdx >= 0)
itr.bpos = blockIdx
block, err := itr.t.block(blockIdx)
if err != nil {
itr.err = err
return
}
itr.bi = block.NewIterator()
itr.bi.Seek(key, ORIGIN)
itr.err = itr.bi.Error()
}
// Seek brings us to a key that is >= input key.
func (itr *TableIterator) seek(key []byte, whence int) {
itr.err = nil
switch whence {
case ORIGIN:
itr.Reset()
case CURRENT:
}
idx := sort.Search(len(itr.t.blockIndex), func(idx int) bool {
ko := itr.t.blockIndex[idx]
return bytes.Compare(ko.key, key) > 0
})
if idx == 0 {
// The smallest key in our table is already strictly > key. We can return that.
// This is like a SeekToFirst.
itr.seekHelper(0, key)
return
}
// block[idx].smallest is > key.
// Since idx>0, we know block[idx-1].smallest is <= key.
// There are two cases.
// 1) Everything in block[idx-1] is strictly < key. In this case, we should go to the first
// element of block[idx].
// 2) Some element in block[idx-1] is >= key. We should go to that element.
itr.seekHelper(idx-1, key)
if itr.err == io.EOF {
// Case 1. Need to visit block[idx].
if idx == len(itr.t.blockIndex) {
// If idx == len(itr.t.blockIndex), then input key is greater than ANY element of table.
// There's nothing we can do. Valid() should return false as we seek to end of table.
return
}
// Since block[idx].smallest is > key. This is essentially a block[idx].SeekToFirst.
itr.seekHelper(idx, key)
}
// Case 2: No need to do anything. We already did the seek in block[idx-1].
}
// Seek will reset iterator and seek to >= key.
func (itr *TableIterator) Seek(key []byte) {
itr.seek(key, ORIGIN)
}
func (itr *TableIterator) Next() {
itr.err = nil
if itr.bpos >= len(itr.t.blockIndex) {
itr.err = io.EOF
return
}
if itr.bi == nil {
block, err := itr.t.block(itr.bpos)
if err != nil {
itr.err = err
return
}
itr.bi = block.NewIterator()
itr.bi.SeekToFirst()
return
}
itr.bi.Next()
if !itr.bi.Valid() {
itr.bpos++
itr.bi = nil
itr.Next()
return
}
}
func (itr *TableIterator) Prev() {
itr.err = nil
if itr.bpos < 0 {
itr.err = io.EOF
return
}
if itr.bi == nil {
block, err := itr.t.block(itr.bpos)
if err != nil {
itr.err = err
return
}
itr.bi = block.NewIterator()
itr.bi.SeekToLast()
return
}
itr.bi.Prev()
if !itr.bi.Valid() {
itr.bpos--
itr.bi = nil
itr.Prev()
return
}
}
func (itr *TableIterator) Key() []byte {
return itr.bi.Key()
}
func (itr *TableIterator) Value() ([]byte, byte) {
v := itr.bi.Value()
return v[1:], v[0]
}
func (itr *TableIterator) Name() string { return "TableIterator" }