LCOV - code coverage report
Current view: top level - pebble/internal/manifest - level_metadata.go (source / functions) Hit Total Coverage
Test: 2024-01-10 08:16Z d50db878 - tests + meta.lcov Lines: 377 462 81.6 %
Date: 2024-01-10 08:17:06 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : package manifest
       6             : 
       7             : import (
       8             :         "bytes"
       9             :         "fmt"
      10             : 
      11             :         "github.com/cockroachdb/pebble/internal/base"
      12             :         "github.com/cockroachdb/pebble/internal/invariants"
      13             : )
      14             : 
      15             : // LevelMetadata contains metadata for all of the files within
      16             : // a level of the LSM.
      17             : type LevelMetadata struct {
      18             :         level     int
      19             :         totalSize uint64
      20             :         // NumVirtual is the number of virtual sstables in the level.
      21             :         NumVirtual uint64
      22             :         // VirtualSize is the size of the virtual sstables in the level.
      23             :         VirtualSize uint64
      24             :         tree        btree
      25             : }
      26             : 
      27             : // clone makes a copy of the level metadata, implicitly increasing the ref
      28             : // count of every file contained within lm.
      29           2 : func (lm *LevelMetadata) clone() LevelMetadata {
      30           2 :         return LevelMetadata{
      31           2 :                 level:       lm.level,
      32           2 :                 totalSize:   lm.totalSize,
      33           2 :                 NumVirtual:  lm.NumVirtual,
      34           2 :                 VirtualSize: lm.VirtualSize,
      35           2 :                 tree:        lm.tree.Clone(),
      36           2 :         }
      37           2 : }
      38             : 
      39           2 : func (lm *LevelMetadata) release() (obsolete []*FileBacking) {
      40           2 :         return lm.tree.Release()
      41           2 : }
      42             : 
      43           2 : func makeLevelMetadata(cmp Compare, level int, files []*FileMetadata) LevelMetadata {
      44           2 :         bcmp := btreeCmpSeqNum
      45           2 :         if level > 0 {
      46           2 :                 bcmp = btreeCmpSmallestKey(cmp)
      47           2 :         }
      48           2 :         var lm LevelMetadata
      49           2 :         lm.level = level
      50           2 :         lm.tree, _ = makeBTree(bcmp, files)
      51           2 :         for _, f := range files {
      52           0 :                 lm.totalSize += f.Size
      53           0 :                 if f.Virtual {
      54           0 :                         lm.NumVirtual++
      55           0 :                         lm.VirtualSize += f.Size
      56           0 :                 }
      57             :         }
      58           2 :         return lm
      59             : }
      60             : 
      61           2 : func makeBTree(cmp btreeCmp, files []*FileMetadata) (btree, LevelSlice) {
      62           2 :         var t btree
      63           2 :         t.cmp = cmp
      64           2 :         for _, f := range files {
      65           2 :                 t.Insert(f)
      66           2 :         }
      67           2 :         return t, newLevelSlice(t.Iter())
      68             : }
      69             : 
      70           2 : func (lm *LevelMetadata) insert(f *FileMetadata) error {
      71           2 :         if err := lm.tree.Insert(f); err != nil {
      72           1 :                 return err
      73           1 :         }
      74           2 :         lm.totalSize += f.Size
      75           2 :         if f.Virtual {
      76           2 :                 lm.NumVirtual++
      77           2 :                 lm.VirtualSize += f.Size
      78           2 :         }
      79           2 :         return nil
      80             : }
      81             : 
      82           2 : func (lm *LevelMetadata) remove(f *FileMetadata) bool {
      83           2 :         lm.totalSize -= f.Size
      84           2 :         if f.Virtual {
      85           2 :                 lm.NumVirtual--
      86           2 :                 lm.VirtualSize -= f.Size
      87           2 :         }
      88           2 :         return lm.tree.Delete(f)
      89             : }
      90             : 
      91             : // Empty indicates whether there are any files in the level.
      92           2 : func (lm *LevelMetadata) Empty() bool {
      93           2 :         return lm.tree.Count() == 0
      94           2 : }
      95             : 
      96             : // Len returns the number of files within the level.
      97           2 : func (lm *LevelMetadata) Len() int {
      98           2 :         return lm.tree.Count()
      99           2 : }
     100             : 
     101             : // Size returns the cumulative size of all the files within the level.
     102           2 : func (lm *LevelMetadata) Size() uint64 {
     103           2 :         return lm.totalSize
     104           2 : }
     105             : 
     106             : // Iter constructs a LevelIterator over the entire level.
     107           2 : func (lm *LevelMetadata) Iter() LevelIterator {
     108           2 :         return LevelIterator{iter: lm.tree.Iter()}
     109           2 : }
     110             : 
     111             : // Slice constructs a slice containing the entire level.
     112           2 : func (lm *LevelMetadata) Slice() LevelSlice {
     113           2 :         return newLevelSlice(lm.tree.Iter())
     114           2 : }
     115             : 
     116             : // Find finds the provided file in the level if it exists.
     117           2 : func (lm *LevelMetadata) Find(cmp base.Compare, m *FileMetadata) *LevelFile {
     118           2 :         iter := lm.Iter()
     119           2 :         if lm.level != 0 {
     120           2 :                 // If lm holds files for levels >0, we can narrow our search by binary
     121           2 :                 // searching by bounds.
     122           2 :                 o := overlaps(iter, cmp, m.Smallest.UserKey,
     123           2 :                         m.Largest.UserKey, m.Largest.IsExclusiveSentinel())
     124           2 :                 iter = o.Iter()
     125           2 :         }
     126           2 :         for f := iter.First(); f != nil; f = iter.Next() {
     127           2 :                 if f == m {
     128           2 :                         lf := iter.Take()
     129           2 :                         return &lf
     130           2 :                 }
     131             :         }
     132           0 :         return nil
     133             : }
     134             : 
     135             : // Annotation lazily calculates and returns the annotation defined by
     136             : // Annotator. The Annotator is used as the key for pre-calculated
     137             : // values, so equal Annotators must be used to avoid duplicate computations
     138             : // and cached annotations. Annotation must not be called concurrently, and in
     139             : // practice this is achieved by requiring callers to hold DB.mu.
     140           2 : func (lm *LevelMetadata) Annotation(annotator Annotator) interface{} {
     141           2 :         if lm.Empty() {
     142           2 :                 return annotator.Zero(nil)
     143           2 :         }
     144           2 :         v, _ := lm.tree.root.Annotation(annotator)
     145           2 :         return v
     146             : }
     147             : 
     148             : // InvalidateAnnotation clears any cached annotations defined by Annotator. The
     149             : // Annotator is used as the key for pre-calculated values, so equal Annotators
     150             : // must be used to clear the appropriate cached annotation. InvalidateAnnotation
     151             : // must not be called concurrently, and in practice this is achieved by
     152             : // requiring callers to hold DB.mu.
     153           1 : func (lm *LevelMetadata) InvalidateAnnotation(annotator Annotator) {
     154           1 :         if lm.Empty() {
     155           0 :                 return
     156           0 :         }
     157           1 :         lm.tree.root.InvalidateAnnotation(annotator)
     158             : }
     159             : 
     160             : // LevelFile holds a file's metadata along with its position
     161             : // within a level of the LSM.
     162             : type LevelFile struct {
     163             :         *FileMetadata
     164             :         slice LevelSlice
     165             : }
     166             : 
     167             : // Slice constructs a LevelSlice containing only this file.
     168           2 : func (lf LevelFile) Slice() LevelSlice {
     169           2 :         return lf.slice
     170           2 : }
     171             : 
     172             : // NewLevelSliceSeqSorted constructs a LevelSlice over the provided files,
     173             : // sorted by the L0 sequence number sort order.
     174             : // TODO(jackson): Can we improve this interface or avoid needing to export
     175             : // a slice constructor like this?
     176           2 : func NewLevelSliceSeqSorted(files []*FileMetadata) LevelSlice {
     177           2 :         tr, slice := makeBTree(btreeCmpSeqNum, files)
     178           2 :         tr.Release()
     179           2 :         slice.verifyInvariants()
     180           2 :         return slice
     181           2 : }
     182             : 
     183             : // NewLevelSliceKeySorted constructs a LevelSlice over the provided files,
     184             : // sorted by the files smallest keys.
     185             : // TODO(jackson): Can we improve this interface or avoid needing to export
     186             : // a slice constructor like this?
     187           2 : func NewLevelSliceKeySorted(cmp base.Compare, files []*FileMetadata) LevelSlice {
     188           2 :         tr, slice := makeBTree(btreeCmpSmallestKey(cmp), files)
     189           2 :         tr.Release()
     190           2 :         slice.verifyInvariants()
     191           2 :         return slice
     192           2 : }
     193             : 
     194             : // NewLevelSliceSpecificOrder constructs a LevelSlice over the provided files,
     195             : // ordering the files by their order in the provided slice. It's used in
     196             : // tests.
     197             : // TODO(jackson): Update tests to avoid requiring this and remove it.
     198           1 : func NewLevelSliceSpecificOrder(files []*FileMetadata) LevelSlice {
     199           1 :         tr, slice := makeBTree(btreeCmpSpecificOrder(files), files)
     200           1 :         tr.Release()
     201           1 :         slice.verifyInvariants()
     202           1 :         return slice
     203           1 : }
     204             : 
     205             : // newLevelSlice constructs a new LevelSlice backed by iter.
     206           2 : func newLevelSlice(iter iterator) LevelSlice {
     207           2 :         s := LevelSlice{iter: iter}
     208           2 :         if iter.r != nil {
     209           2 :                 s.length = iter.r.subtreeCount
     210           2 :         }
     211           2 :         s.verifyInvariants()
     212           2 :         return s
     213             : }
     214             : 
     215             : // newBoundedLevelSlice constructs a new LevelSlice backed by iter and bounded
     216             : // by the provided start and end bounds. The provided startBound and endBound
     217             : // iterators must be iterators over the same B-Tree. Both start and end bounds
     218             : // are inclusive.
     219           2 : func newBoundedLevelSlice(iter iterator, startBound, endBound *iterator) LevelSlice {
     220           2 :         s := LevelSlice{
     221           2 :                 iter:  iter,
     222           2 :                 start: startBound,
     223           2 :                 end:   endBound,
     224           2 :         }
     225           2 :         if iter.valid() {
     226           2 :                 s.length = endBound.countLeft() - startBound.countLeft()
     227           2 :                 // NB: The +1 is a consequence of the end bound being inclusive.
     228           2 :                 if endBound.valid() {
     229           2 :                         s.length++
     230           2 :                 }
     231             :                 // NB: A slice that's empty due to its bounds may have an endBound
     232             :                 // positioned before the startBound due to the inclusive bounds.
     233             :                 // TODO(jackson): Consider refactoring the end boundary to be exclusive;
     234             :                 // it would simplify some areas (eg, here) and complicate others (eg,
     235             :                 // Reslice-ing to grow compactions).
     236           2 :                 if s.length < 0 {
     237           2 :                         s.length = 0
     238           2 :                 }
     239             :         }
     240           2 :         s.verifyInvariants()
     241           2 :         return s
     242             : }
     243             : 
     244             : // LevelSlice contains a slice of the files within a level of the LSM.
     245             : // A LevelSlice is immutable once created, but may be used to construct a
     246             : // mutable LevelIterator over the slice's files.
     247             : //
     248             : // LevelSlices should be constructed through one of the existing constructors,
     249             : // not manually initialized.
     250             : type LevelSlice struct {
     251             :         iter   iterator
     252             :         length int
     253             :         // start and end form the inclusive bounds of a slice of files within a
     254             :         // level of the LSM. They may be nil if the entire B-Tree backing iter is
     255             :         // accessible.
     256             :         start *iterator
     257             :         end   *iterator
     258             : }
     259             : 
     260           2 : func (ls LevelSlice) verifyInvariants() {
     261           2 :         if invariants.Enabled {
     262           2 :                 i := ls.Iter()
     263           2 :                 var length int
     264           2 :                 for f := i.First(); f != nil; f = i.Next() {
     265           2 :                         length++
     266           2 :                 }
     267           2 :                 if ls.length != length {
     268           0 :                         panic(fmt.Sprintf("LevelSlice %s has length %d value; actual length is %d", ls, ls.length, length))
     269             :                 }
     270             :         }
     271             : }
     272             : 
     273             : // Each invokes fn for each element in the slice.
     274           2 : func (ls LevelSlice) Each(fn func(*FileMetadata)) {
     275           2 :         iter := ls.Iter()
     276           2 :         for f := iter.First(); f != nil; f = iter.Next() {
     277           2 :                 fn(f)
     278           2 :         }
     279             : }
     280             : 
     281             : // String implements fmt.Stringer.
     282           1 : func (ls LevelSlice) String() string {
     283           1 :         var buf bytes.Buffer
     284           1 :         fmt.Fprintf(&buf, "%d files: ", ls.length)
     285           1 :         ls.Each(func(f *FileMetadata) {
     286           1 :                 if buf.Len() > 0 {
     287           1 :                         fmt.Fprintf(&buf, " ")
     288           1 :                 }
     289           1 :                 fmt.Fprint(&buf, f)
     290             :         })
     291           1 :         return buf.String()
     292             : }
     293             : 
     294             : // Empty indicates whether the slice contains any files.
     295           2 : func (ls *LevelSlice) Empty() bool {
     296           2 :         return emptyWithBounds(ls.iter, ls.start, ls.end)
     297           2 : }
     298             : 
     299             : // Iter constructs a LevelIterator that iterates over the slice.
     300           2 : func (ls *LevelSlice) Iter() LevelIterator {
     301           2 :         return LevelIterator{
     302           2 :                 start: ls.start,
     303           2 :                 end:   ls.end,
     304           2 :                 iter:  ls.iter.clone(),
     305           2 :         }
     306           2 : }
     307             : 
     308             : // Len returns the number of files in the slice. Its runtime is constant.
     309           2 : func (ls *LevelSlice) Len() int {
     310           2 :         return ls.length
     311           2 : }
     312             : 
     313             : // SizeSum sums the size of all files in the slice. Its runtime is linear in
     314             : // the length of the slice.
     315           2 : func (ls *LevelSlice) SizeSum() uint64 {
     316           2 :         var sum uint64
     317           2 :         iter := ls.Iter()
     318           2 :         for f := iter.First(); f != nil; f = iter.Next() {
     319           2 :                 sum += f.Size
     320           2 :         }
     321           2 :         return sum
     322             : }
     323             : 
     324             : // NumVirtual returns the number of virtual sstables in the level. Its runtime is
     325             : // linear in the length of the slice.
     326           2 : func (ls *LevelSlice) NumVirtual() uint64 {
     327           2 :         var n uint64
     328           2 :         iter := ls.Iter()
     329           2 :         for f := iter.First(); f != nil; f = iter.Next() {
     330           2 :                 if f.Virtual {
     331           2 :                         n++
     332           2 :                 }
     333             :         }
     334           2 :         return n
     335             : }
     336             : 
     337             : // VirtualSizeSum returns the sum of the sizes of the virtual sstables in the
     338             : // level.
     339           2 : func (ls *LevelSlice) VirtualSizeSum() uint64 {
     340           2 :         var sum uint64
     341           2 :         iter := ls.Iter()
     342           2 :         for f := iter.First(); f != nil; f = iter.Next() {
     343           2 :                 if f.Virtual {
     344           2 :                         sum += f.Size
     345           2 :                 }
     346             :         }
     347           2 :         return sum
     348             : }
     349             : 
     350             : // Reslice constructs a new slice backed by the same underlying level, with
     351             : // new start and end positions. Reslice invokes the provided function, passing
     352             : // two LevelIterators: one positioned to i's inclusive start and one
     353             : // positioned to i's inclusive end. The resliceFunc may move either iterator
     354             : // forward or backwards, including beyond the callee's original bounds to
     355             : // capture additional files from the underlying level. Reslice constructs and
     356             : // returns a new LevelSlice with the final bounds of the iterators after
     357             : // calling resliceFunc.
     358           2 : func (ls LevelSlice) Reslice(resliceFunc func(start, end *LevelIterator)) LevelSlice {
     359           2 :         if ls.iter.r == nil {
     360           2 :                 return ls
     361           2 :         }
     362           2 :         var start, end LevelIterator
     363           2 :         if ls.start == nil {
     364           2 :                 start.iter = ls.iter.clone()
     365           2 :                 start.iter.first()
     366           2 :         } else {
     367           2 :                 start.iter = ls.start.clone()
     368           2 :         }
     369           2 :         if ls.end == nil {
     370           2 :                 end.iter = ls.iter.clone()
     371           2 :                 end.iter.last()
     372           2 :         } else {
     373           2 :                 end.iter = ls.end.clone()
     374           2 :         }
     375           2 :         resliceFunc(&start, &end)
     376           2 :         return newBoundedLevelSlice(start.iter.clone(), &start.iter, &end.iter)
     377             : }
     378             : 
     379             : // KeyType is used to specify the type of keys we're looking for in
     380             : // LevelIterator positioning operations. Files not containing any keys of the
     381             : // desired type are skipped.
     382             : type KeyType int8
     383             : 
     384             : const (
     385             :         // KeyTypePointAndRange denotes a search among the entire keyspace, including
     386             :         // both point keys and range keys. No sstables are skipped.
     387             :         KeyTypePointAndRange KeyType = iota
     388             :         // KeyTypePoint denotes a search among the point keyspace. SSTables with no
     389             :         // point keys will be skipped. Note that the point keyspace includes rangedels.
     390             :         KeyTypePoint
     391             :         // KeyTypeRange denotes a search among the range keyspace. SSTables with no
     392             :         // range keys will be skipped.
     393             :         KeyTypeRange
     394             : )
     395             : 
     396             : type keyTypeAnnotator struct{}
     397             : 
     398             : var _ Annotator = keyTypeAnnotator{}
     399             : 
     400           0 : func (k keyTypeAnnotator) Zero(dst interface{}) interface{} {
     401           0 :         var val *KeyType
     402           0 :         if dst != nil {
     403           0 :                 val = dst.(*KeyType)
     404           0 :         } else {
     405           0 :                 val = new(KeyType)
     406           0 :         }
     407           0 :         *val = KeyTypePoint
     408           0 :         return val
     409             : }
     410             : 
     411           0 : func (k keyTypeAnnotator) Accumulate(m *FileMetadata, dst interface{}) (interface{}, bool) {
     412           0 :         v := dst.(*KeyType)
     413           0 :         switch *v {
     414           0 :         case KeyTypePoint:
     415           0 :                 if m.HasRangeKeys {
     416           0 :                         *v = KeyTypePointAndRange
     417           0 :                 }
     418           0 :         case KeyTypePointAndRange:
     419             :                 // Do nothing.
     420           0 :         default:
     421           0 :                 panic("unexpected key type")
     422             :         }
     423           0 :         return v, true
     424             : }
     425             : 
     426           0 : func (k keyTypeAnnotator) Merge(src interface{}, dst interface{}) interface{} {
     427           0 :         v := dst.(*KeyType)
     428           0 :         srcVal := src.(*KeyType)
     429           0 :         switch *v {
     430           0 :         case KeyTypePoint:
     431           0 :                 if *srcVal == KeyTypePointAndRange {
     432           0 :                         *v = KeyTypePointAndRange
     433           0 :                 }
     434           0 :         case KeyTypePointAndRange:
     435             :                 // Do nothing.
     436           0 :         default:
     437           0 :                 panic("unexpected key type")
     438             :         }
     439           0 :         return v
     440             : }
     441             : 
     442             : // LevelIterator iterates over a set of files' metadata. Its zero value is an
     443             : // empty iterator.
     444             : type LevelIterator struct {
     445             :         iter   iterator
     446             :         start  *iterator
     447             :         end    *iterator
     448             :         filter KeyType
     449             : }
     450             : 
     451           0 : func (i LevelIterator) String() string {
     452           0 :         var buf bytes.Buffer
     453           0 :         iter := i.iter.clone()
     454           0 :         iter.first()
     455           0 :         iter.prev()
     456           0 :         if i.iter.pos == -1 {
     457           0 :                 fmt.Fprint(&buf, "(<start>)*")
     458           0 :         }
     459           0 :         iter.next()
     460           0 :         for ; iter.valid(); iter.next() {
     461           0 :                 if buf.Len() > 0 {
     462           0 :                         fmt.Fprint(&buf, "   ")
     463           0 :                 }
     464             : 
     465           0 :                 if i.start != nil && cmpIter(iter, *i.start) == 0 {
     466           0 :                         fmt.Fprintf(&buf, " [ ")
     467           0 :                 }
     468           0 :                 isCurrentPos := cmpIter(iter, i.iter) == 0
     469           0 :                 if isCurrentPos {
     470           0 :                         fmt.Fprint(&buf, " ( ")
     471           0 :                 }
     472           0 :                 fmt.Fprint(&buf, iter.cur().String())
     473           0 :                 if isCurrentPos {
     474           0 :                         fmt.Fprint(&buf, " )*")
     475           0 :                 }
     476           0 :                 if i.end != nil && cmpIter(iter, *i.end) == 0 {
     477           0 :                         fmt.Fprintf(&buf, " ]")
     478           0 :                 }
     479             :         }
     480           0 :         if i.iter.n != nil && i.iter.pos >= i.iter.n.count {
     481           0 :                 if buf.Len() > 0 {
     482           0 :                         fmt.Fprint(&buf, "   ")
     483           0 :                 }
     484           0 :                 fmt.Fprint(&buf, "(<end>)*")
     485             :         }
     486           0 :         return buf.String()
     487             : }
     488             : 
     489             : // Clone copies the iterator, returning an independent iterator at the same
     490             : // position.
     491           2 : func (i *LevelIterator) Clone() LevelIterator {
     492           2 :         if i.iter.r == nil {
     493           2 :                 return *i
     494           2 :         }
     495             :         // The start and end iterators are not cloned and are treated as
     496             :         // immutable.
     497           2 :         return LevelIterator{
     498           2 :                 iter:   i.iter.clone(),
     499           2 :                 start:  i.start,
     500           2 :                 end:    i.end,
     501           2 :                 filter: i.filter,
     502           2 :         }
     503             : }
     504             : 
     505             : // Current returns the item at the current iterator position.
     506             : //
     507             : // Current is deprecated. Callers should instead use the return value of a
     508             : // positioning operation.
     509           2 : func (i *LevelIterator) Current() *FileMetadata {
     510           2 :         if !i.iter.valid() ||
     511           2 :                 (i.end != nil && cmpIter(i.iter, *i.end) > 0) ||
     512           2 :                 (i.start != nil && cmpIter(i.iter, *i.start) < 0) {
     513           2 :                 return nil
     514           2 :         }
     515           2 :         return i.iter.cur()
     516             : }
     517             : 
     518           2 : func (i *LevelIterator) empty() bool {
     519           2 :         return emptyWithBounds(i.iter, i.start, i.end)
     520           2 : }
     521             : 
     522             : // Filter clones the iterator and sets the desired KeyType as the key to filter
     523             : // files on.
     524           2 : func (i *LevelIterator) Filter(keyType KeyType) LevelIterator {
     525           2 :         l := i.Clone()
     526           2 :         l.filter = keyType
     527           2 :         return l
     528           2 : }
     529             : 
     530           2 : func emptyWithBounds(i iterator, start, end *iterator) bool {
     531           2 :         // If i.r is nil, the iterator was constructed from an empty btree.
     532           2 :         // If the end bound is before the start bound, the bounds represent an
     533           2 :         // empty slice of the B-Tree.
     534           2 :         return i.r == nil || (start != nil && end != nil && cmpIter(*end, *start) < 0)
     535           2 : }
     536             : 
     537             : // First seeks to the first file in the iterator and returns it.
     538           2 : func (i *LevelIterator) First() *FileMetadata {
     539           2 :         if i.empty() {
     540           2 :                 return nil
     541           2 :         }
     542           2 :         if i.start != nil {
     543           2 :                 i.iter = i.start.clone()
     544           2 :         } else {
     545           2 :                 i.iter.first()
     546           2 :         }
     547           2 :         if !i.iter.valid() {
     548           2 :                 return nil
     549           2 :         }
     550           2 :         return i.skipFilteredForward(i.iter.cur())
     551             : }
     552             : 
     553             : // Last seeks to the last file in the iterator and returns it.
     554           2 : func (i *LevelIterator) Last() *FileMetadata {
     555           2 :         if i.empty() {
     556           2 :                 return nil
     557           2 :         }
     558           2 :         if i.end != nil {
     559           2 :                 i.iter = i.end.clone()
     560           2 :         } else {
     561           2 :                 i.iter.last()
     562           2 :         }
     563           2 :         if !i.iter.valid() {
     564           0 :                 return nil
     565           0 :         }
     566           2 :         return i.skipFilteredBackward(i.iter.cur())
     567             : }
     568             : 
     569             : // Next advances the iterator to the next file and returns it.
     570           2 : func (i *LevelIterator) Next() *FileMetadata {
     571           2 :         if i.iter.r == nil {
     572           0 :                 return nil
     573           0 :         }
     574           2 :         if invariants.Enabled && (i.iter.pos >= i.iter.n.count || (i.end != nil && cmpIter(i.iter, *i.end) > 0)) {
     575           0 :                 panic("pebble: cannot next forward-exhausted iterator")
     576             :         }
     577           2 :         i.iter.next()
     578           2 :         if !i.iter.valid() {
     579           2 :                 return nil
     580           2 :         }
     581           2 :         return i.skipFilteredForward(i.iter.cur())
     582             : }
     583             : 
     584             : // Prev moves the iterator the previous file and returns it.
     585           2 : func (i *LevelIterator) Prev() *FileMetadata {
     586           2 :         if i.iter.r == nil {
     587           2 :                 return nil
     588           2 :         }
     589           2 :         if invariants.Enabled && (i.iter.pos < 0 || (i.start != nil && cmpIter(i.iter, *i.start) < 0)) {
     590           0 :                 panic("pebble: cannot prev backward-exhausted iterator")
     591             :         }
     592           2 :         i.iter.prev()
     593           2 :         if !i.iter.valid() {
     594           2 :                 return nil
     595           2 :         }
     596           2 :         return i.skipFilteredBackward(i.iter.cur())
     597             : }
     598             : 
     599             : // SeekGE seeks to the first file in the iterator's file set with a largest
     600             : // user key greater than or equal to the provided user key. The iterator must
     601             : // have been constructed from L1+, because it requires the underlying files to
     602             : // be sorted by user keys and non-overlapping.
     603           2 : func (i *LevelIterator) SeekGE(cmp Compare, userKey []byte) *FileMetadata {
     604           2 :         // TODO(jackson): Assert that i.iter.cmp == btreeCmpSmallestKey.
     605           2 :         if i.iter.r == nil {
     606           2 :                 return nil
     607           2 :         }
     608           2 :         m := i.seek(func(m *FileMetadata) bool {
     609           2 :                 return cmp(m.Largest.UserKey, userKey) >= 0
     610           2 :         })
     611           2 :         if i.filter != KeyTypePointAndRange && m != nil {
     612           2 :                 b, ok := m.LargestBound(i.filter)
     613           2 :                 if !ok {
     614           2 :                         m = i.Next()
     615           2 :                 } else if c := cmp(b.UserKey, userKey); c < 0 || c == 0 && b.IsExclusiveSentinel() {
     616           2 :                         // This file does not contain any keys of the type ≥ lower. It
     617           2 :                         // should be filtered, even though it does contain point keys.
     618           2 :                         m = i.Next()
     619           2 :                 }
     620             :         }
     621           2 :         return i.skipFilteredForward(m)
     622             : }
     623             : 
     624             : // SeekLT seeks to the last file in the iterator's file set with a smallest
     625             : // user key less than the provided user key. The iterator must have been
     626             : // constructed from L1+, because it requires the underlying files to be sorted
     627             : // by user keys and non-overlapping.
     628           2 : func (i *LevelIterator) SeekLT(cmp Compare, userKey []byte) *FileMetadata {
     629           2 :         // TODO(jackson): Assert that i.iter.cmp == btreeCmpSmallestKey.
     630           2 :         if i.iter.r == nil {
     631           2 :                 return nil
     632           2 :         }
     633           2 :         i.seek(func(m *FileMetadata) bool {
     634           2 :                 return cmp(m.Smallest.UserKey, userKey) >= 0
     635           2 :         })
     636           2 :         m := i.Prev()
     637           2 :         // Although i.Prev() guarantees that the current file contains keys of the
     638           2 :         // relevant type, it doesn't guarantee that the keys of the relevant type
     639           2 :         // are < userKey.
     640           2 :         if i.filter != KeyTypePointAndRange && m != nil {
     641           2 :                 b, ok := m.SmallestBound(i.filter)
     642           2 :                 if !ok {
     643           0 :                         panic("unreachable")
     644             :                 }
     645           2 :                 if c := cmp(b.UserKey, userKey); c >= 0 {
     646           2 :                         // This file does not contain any keys of the type ≥ lower. It
     647           2 :                         // should be filtered, even though it does contain point keys.
     648           2 :                         m = i.Prev()
     649           2 :                 }
     650             :         }
     651           2 :         return i.skipFilteredBackward(m)
     652             : }
     653             : 
     654             : // skipFilteredForward takes the file metadata at the iterator's current
     655             : // position, and skips forward if the current key-type filter (i.filter)
     656             : // excludes the file. It skips until it finds an unfiltered file or exhausts the
     657             : // level. If lower is != nil, skipFilteredForward skips any files that do not
     658             : // contain keys with the provided key-type ≥ lower.
     659             : //
     660             : // skipFilteredForward also enforces the upper bound, returning nil if at any
     661             : // point the upper bound is exceeded.
     662           2 : func (i *LevelIterator) skipFilteredForward(meta *FileMetadata) *FileMetadata {
     663           2 :         for meta != nil && !meta.ContainsKeyType(i.filter) {
     664           2 :                 i.iter.next()
     665           2 :                 if !i.iter.valid() {
     666           2 :                         meta = nil
     667           2 :                 } else {
     668           2 :                         meta = i.iter.cur()
     669           2 :                 }
     670             :         }
     671           2 :         if meta != nil && i.end != nil && cmpIter(i.iter, *i.end) > 0 {
     672           2 :                 // Exceeded upper bound.
     673           2 :                 meta = nil
     674           2 :         }
     675           2 :         return meta
     676             : }
     677             : 
     678             : // skipFilteredBackward takes the file metadata at the iterator's current
     679             : // position, and skips backward if the current key-type filter (i.filter)
     680             : // excludes the file. It skips until it finds an unfiltered file or exhausts the
     681             : // level. If upper is != nil, skipFilteredBackward skips any files that do not
     682             : // contain keys with the provided key-type < upper.
     683             : //
     684             : // skipFilteredBackward also enforces the lower bound, returning nil if at any
     685             : // point the lower bound is exceeded.
     686           2 : func (i *LevelIterator) skipFilteredBackward(meta *FileMetadata) *FileMetadata {
     687           2 :         for meta != nil && !meta.ContainsKeyType(i.filter) {
     688           2 :                 i.iter.prev()
     689           2 :                 if !i.iter.valid() {
     690           2 :                         meta = nil
     691           2 :                 } else {
     692           2 :                         meta = i.iter.cur()
     693           2 :                 }
     694             :         }
     695           2 :         if meta != nil && i.start != nil && cmpIter(i.iter, *i.start) < 0 {
     696           0 :                 // Exceeded lower bound.
     697           0 :                 meta = nil
     698           0 :         }
     699           2 :         return meta
     700             : }
     701             : 
     702           2 : func (i *LevelIterator) seek(fn func(*FileMetadata) bool) *FileMetadata {
     703           2 :         i.iter.seek(fn)
     704           2 : 
     705           2 :         // i.iter.seek seeked in the unbounded underlying B-Tree. If the iterator
     706           2 :         // has start or end bounds, we may have exceeded them. Reset to the bounds
     707           2 :         // if necessary.
     708           2 :         //
     709           2 :         // NB: The LevelIterator and LevelSlice semantics require that a bounded
     710           2 :         // LevelIterator/LevelSlice containing files x0, x1, ..., xn behave
     711           2 :         // identically to an unbounded LevelIterator/LevelSlice of a B-Tree
     712           2 :         // containing x0, x1, ..., xn. In other words, any files outside the
     713           2 :         // LevelIterator's bounds should not influence the iterator's behavior.
     714           2 :         // When seeking, this means a SeekGE that seeks beyond the end bound,
     715           2 :         // followed by a Prev should return the last element within bounds.
     716           2 :         if i.end != nil && cmpIter(i.iter, *i.end) > 0 {
     717           2 :                 i.iter = i.end.clone()
     718           2 :                 // Since seek(fn) positioned beyond i.end, we know there is nothing to
     719           2 :                 // return within bounds.
     720           2 :                 i.iter.next()
     721           2 :                 return nil
     722           2 :         } else if i.start != nil && cmpIter(i.iter, *i.start) < 0 {
     723           2 :                 i.iter = i.start.clone()
     724           2 :         }
     725           2 :         if !i.iter.valid() {
     726           2 :                 return nil
     727           2 :         }
     728           2 :         return i.iter.cur()
     729             : }
     730             : 
     731             : // Take constructs a LevelFile containing the file at the iterator's current
     732             : // position. Take panics if the iterator is not currently positioned over a
     733             : // file.
     734           2 : func (i *LevelIterator) Take() LevelFile {
     735           2 :         m := i.Current()
     736           2 :         if m == nil {
     737           0 :                 panic("Take called on invalid LevelIterator")
     738             :         }
     739             :         // LevelSlice's start and end fields are immutable and are positioned to
     740             :         // the same position for a LevelFile because they're inclusive, so we can
     741             :         // share one iterator stack between the two bounds.
     742           2 :         boundsIter := i.iter.clone()
     743           2 :         s := newBoundedLevelSlice(i.iter.clone(), &boundsIter, &boundsIter)
     744           2 :         return LevelFile{
     745           2 :                 FileMetadata: m,
     746           2 :                 slice:        s,
     747           2 :         }
     748             : }

Generated by: LCOV version 1.14