package table
import (
"bytes"
"fmt"
"io"
"sort"
"github.com/dgraph-io/badger/v4/fb"
"github.com/dgraph-io/badger/v4/y"
)
type blockIterator struct {
data []byte
idx int
err error
baseKey []byte
key []byte
val []byte
entryOffsets []uint32
block *Block
tableID uint64
blockID int
prevOverlap uint16
}
func (itr *blockIterator ) setBlock (b *Block ) {
itr .block .decrRef ()
itr .block = b
itr .err = nil
itr .idx = 0
itr .baseKey = itr .baseKey [:0 ]
itr .prevOverlap = 0
itr .key = itr .key [:0 ]
itr .val = itr .val [:0 ]
itr .data = b .data [:b .entriesIndexStart ]
itr .entryOffsets = b .entryOffsets
}
func (itr *blockIterator ) setIdx (i int ) {
itr .idx = i
if i >= len (itr .entryOffsets ) || i < 0 {
itr .err = io .EOF
return
}
itr .err = nil
startOffset := int (itr .entryOffsets [i ])
if len (itr .baseKey ) == 0 {
var baseHeader header
baseHeader .Decode (itr .data )
itr .baseKey = itr .data [headerSize : headerSize +baseHeader .diff ]
}
var endOffset int
if itr .idx +1 == len (itr .entryOffsets ) {
endOffset = len (itr .data )
} else {
endOffset = int (itr .entryOffsets [itr .idx +1 ])
}
defer func () {
if r := recover (); r != nil {
var debugBuf bytes .Buffer
fmt .Fprintf (&debugBuf , "==== Recovered====\n" )
fmt .Fprintf (&debugBuf , "Table ID: %d\nBlock ID: %d\nEntry Idx: %d\nData len: %d\n" +
"StartOffset: %d\nEndOffset: %d\nEntryOffsets len: %d\nEntryOffsets: %v\n" ,
itr .tableID , itr .blockID , itr .idx , len (itr .data ), startOffset , endOffset ,
len (itr .entryOffsets ), itr .entryOffsets )
panic (debugBuf .String ())
}
}()
entryData := itr .data [startOffset :endOffset ]
var h header
h .Decode (entryData )
if h .overlap > itr .prevOverlap {
itr .key = append (itr .key [:itr .prevOverlap ], itr .baseKey [itr .prevOverlap :h .overlap ]...)
}
itr .prevOverlap = h .overlap
valueOff := headerSize + h .diff
diffKey := entryData [headerSize :valueOff ]
itr .key = append (itr .key [:h .overlap ], diffKey ...)
itr .val = entryData [valueOff :]
}
func (itr *blockIterator ) Valid () bool {
return itr != nil && itr .err == nil
}
func (itr *blockIterator ) Error () error {
return itr .err
}
func (itr *blockIterator ) Close () {
itr .block .decrRef ()
}
var (
origin = 0
current = 1
)
func (itr *blockIterator ) seek (key []byte , whence int ) {
itr .err = nil
startIndex := 0
switch whence {
case origin :
case current :
startIndex = itr .idx
}
foundEntryIdx := sort .Search (len (itr .entryOffsets ), func (idx int ) bool {
if idx < startIndex {
return false
}
itr .setIdx (idx )
return y .CompareKeys (itr .key , key ) >= 0
})
itr .setIdx (foundEntryIdx )
}
func (itr *blockIterator ) seekToFirst () {
itr .setIdx (0 )
}
func (itr *blockIterator ) seekToLast () {
itr .setIdx (len (itr .entryOffsets ) - 1 )
}
func (itr *blockIterator ) next () {
itr .setIdx (itr .idx + 1 )
}
func (itr *blockIterator ) prev () {
itr .setIdx (itr .idx - 1 )
}
type Iterator struct {
t *Table
bpos int
bi blockIterator
err error
opt int
}
func (t *Table ) NewIterator (opt int ) *Iterator {
t .IncrRef ()
ti := &Iterator {t : t , opt : opt }
return ti
}
func (itr *Iterator ) Close () error {
itr .bi .Close ()
return itr .t .DecrRef ()
}
func (itr *Iterator ) reset () {
itr .bpos = 0
itr .err = nil
}
func (itr *Iterator ) Valid () bool {
return itr .err == nil
}
func (itr *Iterator ) useCache () bool {
return itr .opt &NOCACHE == 0
}
func (itr *Iterator ) seekToFirst () {
numBlocks := itr .t .offsetsLength ()
if numBlocks == 0 {
itr .err = io .EOF
return
}
itr .bpos = 0
block , err := itr .t .block (itr .bpos , itr .useCache ())
if err != nil {
itr .err = err
return
}
itr .bi .tableID = itr .t .id
itr .bi .blockID = itr .bpos
itr .bi .setBlock (block )
itr .bi .seekToFirst ()
itr .err = itr .bi .Error ()
}
func (itr *Iterator ) seekToLast () {
numBlocks := itr .t .offsetsLength ()
if numBlocks == 0 {
itr .err = io .EOF
return
}
itr .bpos = numBlocks - 1
block , err := itr .t .block (itr .bpos , itr .useCache ())
if err != nil {
itr .err = err
return
}
itr .bi .tableID = itr .t .id
itr .bi .blockID = itr .bpos
itr .bi .setBlock (block )
itr .bi .seekToLast ()
itr .err = itr .bi .Error ()
}
func (itr *Iterator ) seekHelper (blockIdx int , key []byte ) {
itr .bpos = blockIdx
block , err := itr .t .block (blockIdx , itr .useCache ())
if err != nil {
itr .err = err
return
}
itr .bi .tableID = itr .t .id
itr .bi .blockID = itr .bpos
itr .bi .setBlock (block )
itr .bi .seek (key , origin )
itr .err = itr .bi .Error ()
}
func (itr *Iterator ) seekFrom (key []byte , whence int ) {
itr .err = nil
switch whence {
case origin :
itr .reset ()
case current :
}
var ko fb .BlockOffset
idx := sort .Search (itr .t .offsetsLength (), func (idx int ) bool {
y .AssertTrue (itr .t .offsets (&ko , idx ))
return y .CompareKeys (ko .KeyBytes (), key ) > 0
})
if idx == 0 {
itr .seekHelper (0 , key )
return
}
itr .seekHelper (idx -1 , key )
if itr .err == io .EOF {
if idx == itr .t .offsetsLength () {
return
}
itr .seekHelper (idx , key )
}
}
func (itr *Iterator ) seek (key []byte ) {
itr .seekFrom (key , origin )
}
func (itr *Iterator ) seekForPrev (key []byte ) {
itr .seekFrom (key , origin )
if !bytes .Equal (itr .Key (), key ) {
itr .prev ()
}
}
func (itr *Iterator ) next () {
itr .err = nil
if itr .bpos >= itr .t .offsetsLength () {
itr .err = io .EOF
return
}
if len (itr .bi .data ) == 0 {
block , err := itr .t .block (itr .bpos , itr .useCache ())
if err != nil {
itr .err = err
return
}
itr .bi .tableID = itr .t .id
itr .bi .blockID = itr .bpos
itr .bi .setBlock (block )
itr .bi .seekToFirst ()
itr .err = itr .bi .Error ()
return
}
itr .bi .next ()
if !itr .bi .Valid () {
itr .bpos ++
itr .bi .data = nil
itr .next ()
return
}
}
func (itr *Iterator ) prev () {
itr .err = nil
if itr .bpos < 0 {
itr .err = io .EOF
return
}
if len (itr .bi .data ) == 0 {
block , err := itr .t .block (itr .bpos , itr .useCache ())
if err != nil {
itr .err = err
return
}
itr .bi .tableID = itr .t .id
itr .bi .blockID = itr .bpos
itr .bi .setBlock (block )
itr .bi .seekToLast ()
itr .err = itr .bi .Error ()
return
}
itr .bi .prev ()
if !itr .bi .Valid () {
itr .bpos --
itr .bi .data = nil
itr .prev ()
return
}
}
func (itr *Iterator ) Key () []byte {
return itr .bi .key
}
func (itr *Iterator ) Value () (ret y .ValueStruct ) {
ret .Decode (itr .bi .val )
return
}
func (itr *Iterator ) ValueCopy () (ret y .ValueStruct ) {
dst := y .Copy (itr .bi .val )
ret .Decode (dst )
return
}
func (itr *Iterator ) Next () {
if itr .opt &REVERSED == 0 {
itr .next ()
} else {
itr .prev ()
}
}
func (itr *Iterator ) Rewind () {
if itr .opt &REVERSED == 0 {
itr .seekToFirst ()
} else {
itr .seekToLast ()
}
}
func (itr *Iterator ) Seek (key []byte ) {
if itr .opt &REVERSED == 0 {
itr .seek (key )
} else {
itr .seekForPrev (key )
}
}
var (
REVERSED int = 2
NOCACHE int = 4
)
type ConcatIterator struct {
idx int
cur *Iterator
iters []*Iterator
tables []*Table
options int
}
func NewConcatIterator (tbls []*Table , opt int ) *ConcatIterator {
iters := make ([]*Iterator , len (tbls ))
for i := 0 ; i < len (tbls ); i ++ {
tbls [i ].IncrRef ()
}
return &ConcatIterator {
options : opt ,
iters : iters ,
tables : tbls ,
idx : -1 ,
}
}
func (s *ConcatIterator ) setIdx (idx int ) {
s .idx = idx
if idx < 0 || idx >= len (s .iters ) {
s .cur = nil
return
}
if s .iters [idx ] == nil {
s .iters [idx ] = s .tables [idx ].NewIterator (s .options )
}
s .cur = s .iters [s .idx ]
}
func (s *ConcatIterator ) Rewind () {
if len (s .iters ) == 0 {
return
}
if s .options &REVERSED == 0 {
s .setIdx (0 )
} else {
s .setIdx (len (s .iters ) - 1 )
}
s .cur .Rewind ()
}
func (s *ConcatIterator ) Valid () bool {
return s .cur != nil && s .cur .Valid ()
}
func (s *ConcatIterator ) Key () []byte {
return s .cur .Key ()
}
func (s *ConcatIterator ) Value () y .ValueStruct {
return s .cur .Value ()
}
func (s *ConcatIterator ) Seek (key []byte ) {
var idx int
if s .options &REVERSED == 0 {
idx = sort .Search (len (s .tables ), func (i int ) bool {
return y .CompareKeys (s .tables [i ].Biggest (), key ) >= 0
})
} else {
n := len (s .tables )
idx = n - 1 - sort .Search (n , func (i int ) bool {
return y .CompareKeys (s .tables [n -1 -i ].Smallest (), key ) <= 0
})
}
if idx >= len (s .tables ) || idx < 0 {
s .setIdx (-1 )
return
}
s .setIdx (idx )
s .cur .Seek (key )
}
func (s *ConcatIterator ) Next () {
s .cur .Next ()
if s .cur .Valid () {
return
}
for {
if s .options &REVERSED == 0 {
s .setIdx (s .idx + 1 )
} else {
s .setIdx (s .idx - 1 )
}
if s .cur == nil {
return
}
s .cur .Rewind ()
if s .cur .Valid () {
break
}
}
}
func (s *ConcatIterator ) Close () error {
for _ , t := range s .tables {
if err := t .DecrRef (); err != nil {
return err
}
}
for _ , it := range s .iters {
if it == nil {
continue
}
if err := it .Close (); err != nil {
return y .Wrap (err , "ConcatIterator" )
}
}
return nil
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .