LCOV - code coverage report
Current view: top level - pebble/internal/manifest - level_metadata.go (source / functions) Hit Total Coverage
Test: 2024-04-15 08:15Z c34894c4 - tests + meta.lcov Lines: 387 436 88.8 %
Date: 2024-04-15 08:16:52 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             :         "reflect"
      11             : 
      12             :         "github.com/cockroachdb/pebble/internal/base"
      13             :         "github.com/cockroachdb/pebble/internal/invariants"
      14             : )
      15             : 
      16             : // LevelMetadata contains metadata for all of the files within
      17             : // a level of the LSM.
      18             : type LevelMetadata struct {
      19             :         level     int
      20             :         totalSize uint64
      21             :         // NumVirtual is the number of virtual sstables in the level.
      22             :         NumVirtual uint64
      23             :         // VirtualSize is the size of the virtual sstables in the level.
      24             :         VirtualSize uint64
      25             :         tree        btree
      26             : }
      27             : 
      28             : // clone makes a copy of the level metadata, implicitly increasing the ref
      29             : // count of every file contained within lm.
      30           2 : func (lm *LevelMetadata) clone() LevelMetadata {
      31           2 :         return LevelMetadata{
      32           2 :                 level:       lm.level,
      33           2 :                 totalSize:   lm.totalSize,
      34           2 :                 NumVirtual:  lm.NumVirtual,
      35           2 :                 VirtualSize: lm.VirtualSize,
      36           2 :                 tree:        lm.tree.Clone(),
      37           2 :         }
      38           2 : }
      39             : 
      40           2 : func (lm *LevelMetadata) release() (obsolete []*FileBacking) {
      41           2 :         return lm.tree.Release()
      42           2 : }
      43             : 
      44           2 : func makeLevelMetadata(cmp Compare, level int, files []*FileMetadata) LevelMetadata {
      45           2 :         bcmp := btreeCmpSeqNum
      46           2 :         if level > 0 {
      47           2 :                 bcmp = btreeCmpSmallestKey(cmp)
      48           2 :         }
      49           2 :         var lm LevelMetadata
      50           2 :         lm.level = level
      51           2 :         lm.tree, _ = makeBTree(bcmp, files)
      52           2 :         for _, f := range files {
      53           1 :                 lm.totalSize += f.Size
      54           1 :                 if f.Virtual {
      55           0 :                         lm.NumVirtual++
      56           0 :                         lm.VirtualSize += f.Size
      57           0 :                 }
      58             :         }
      59           2 :         return lm
      60             : }
      61             : 
      62           2 : func makeBTree(cmp btreeCmp, files []*FileMetadata) (btree, LevelSlice) {
      63           2 :         var t btree
      64           2 :         t.cmp = cmp
      65           2 :         for _, f := range files {
      66           2 :                 t.Insert(f)
      67           2 :         }
      68           2 :         return t, newLevelSlice(t.Iter())
      69             : }
      70             : 
      71           2 : func (lm *LevelMetadata) insert(f *FileMetadata) error {
      72           2 :         if err := lm.tree.Insert(f); err != nil {
      73           1 :                 return err
      74           1 :         }
      75           2 :         lm.totalSize += f.Size
      76           2 :         if f.Virtual {
      77           2 :                 lm.NumVirtual++
      78           2 :                 lm.VirtualSize += f.Size
      79           2 :         }
      80           2 :         return nil
      81             : }
      82             : 
      83           2 : func (lm *LevelMetadata) remove(f *FileMetadata) bool {
      84           2 :         lm.totalSize -= f.Size
      85           2 :         if f.Virtual {
      86           2 :                 lm.NumVirtual--
      87           2 :                 lm.VirtualSize -= f.Size
      88           2 :         }
      89           2 :         return lm.tree.Delete(f)
      90             : }
      91             : 
      92             : // Empty indicates whether there are any files in the level.
      93           2 : func (lm *LevelMetadata) Empty() bool {
      94           2 :         return lm.tree.Count() == 0
      95           2 : }
      96             : 
      97             : // Len returns the number of files within the level.
      98           2 : func (lm *LevelMetadata) Len() int {
      99           2 :         return lm.tree.Count()
     100           2 : }
     101             : 
     102             : // Size returns the cumulative size of all the files within the level.
     103           2 : func (lm *LevelMetadata) Size() uint64 {
     104           2 :         return lm.totalSize
     105           2 : }
     106             : 
     107             : // Iter constructs a LevelIterator over the entire level.
     108           2 : func (lm *LevelMetadata) Iter() LevelIterator {
     109           2 :         return LevelIterator{iter: lm.tree.Iter()}
     110           2 : }
     111             : 
     112             : // Slice constructs a slice containing the entire level.
     113           2 : func (lm *LevelMetadata) Slice() LevelSlice {
     114           2 :         return newLevelSlice(lm.tree.Iter())
     115           2 : }
     116             : 
     117             : // Find finds the provided file in the level if it exists.
     118           2 : func (lm *LevelMetadata) Find(cmp base.Compare, m *FileMetadata) *LevelFile {
     119           2 :         iter := lm.Iter()
     120           2 :         if lm.level != 0 {
     121           2 :                 // If lm holds files for levels >0, we can narrow our search by binary
     122           2 :                 // searching by bounds.
     123           2 :                 o := overlaps(iter, cmp, m.UserKeyBounds())
     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             : // LevelIterator iterates over a set of files' metadata. Its zero value is an
     397             : // empty iterator.
     398             : type LevelIterator struct {
     399             :         iter   iterator
     400             :         start  *iterator
     401             :         end    *iterator
     402             :         filter KeyType
     403             : }
     404             : 
     405           0 : func (i LevelIterator) String() string {
     406           0 :         var buf bytes.Buffer
     407           0 :         iter := i.iter.clone()
     408           0 :         iter.first()
     409           0 :         iter.prev()
     410           0 :         if i.iter.pos == -1 {
     411           0 :                 fmt.Fprint(&buf, "(<start>)*")
     412           0 :         }
     413           0 :         iter.next()
     414           0 :         for ; iter.valid(); iter.next() {
     415           0 :                 if buf.Len() > 0 {
     416           0 :                         fmt.Fprint(&buf, "   ")
     417           0 :                 }
     418             : 
     419           0 :                 if i.start != nil && cmpIter(iter, *i.start) == 0 {
     420           0 :                         fmt.Fprintf(&buf, " [ ")
     421           0 :                 }
     422           0 :                 isCurrentPos := cmpIter(iter, i.iter) == 0
     423           0 :                 if isCurrentPos {
     424           0 :                         fmt.Fprint(&buf, " ( ")
     425           0 :                 }
     426           0 :                 fmt.Fprint(&buf, iter.cur().String())
     427           0 :                 if isCurrentPos {
     428           0 :                         fmt.Fprint(&buf, " )*")
     429           0 :                 }
     430           0 :                 if i.end != nil && cmpIter(iter, *i.end) == 0 {
     431           0 :                         fmt.Fprintf(&buf, " ]")
     432           0 :                 }
     433             :         }
     434           0 :         if i.iter.n != nil && i.iter.pos >= i.iter.n.count {
     435           0 :                 if buf.Len() > 0 {
     436           0 :                         fmt.Fprint(&buf, "   ")
     437           0 :                 }
     438           0 :                 fmt.Fprint(&buf, "(<end>)*")
     439             :         }
     440           0 :         return buf.String()
     441             : }
     442             : 
     443             : // Clone copies the iterator, returning an independent iterator at the same
     444             : // position.
     445           2 : func (i *LevelIterator) Clone() LevelIterator {
     446           2 :         if i.iter.r == nil {
     447           2 :                 return *i
     448           2 :         }
     449             :         // The start and end iterators are not cloned and are treated as
     450             :         // immutable.
     451           2 :         return LevelIterator{
     452           2 :                 iter:   i.iter.clone(),
     453           2 :                 start:  i.start,
     454           2 :                 end:    i.end,
     455           2 :                 filter: i.filter,
     456           2 :         }
     457             : }
     458             : 
     459             : // Current returns the item at the current iterator position.
     460             : //
     461             : // Current is deprecated. Callers should instead use the return value of a
     462             : // positioning operation.
     463           2 : func (i *LevelIterator) Current() *FileMetadata {
     464           2 :         if !i.iter.valid() ||
     465           2 :                 (i.end != nil && cmpIter(i.iter, *i.end) > 0) ||
     466           2 :                 (i.start != nil && cmpIter(i.iter, *i.start) < 0) {
     467           2 :                 return nil
     468           2 :         }
     469           2 :         return i.iter.cur()
     470             : }
     471             : 
     472           2 : func (i *LevelIterator) empty() bool {
     473           2 :         return emptyWithBounds(i.iter, i.start, i.end)
     474           2 : }
     475             : 
     476             : // Filter clones the iterator and sets the desired KeyType as the key to filter
     477             : // files on.
     478           2 : func (i *LevelIterator) Filter(keyType KeyType) LevelIterator {
     479           2 :         l := i.Clone()
     480           2 :         l.filter = keyType
     481           2 :         return l
     482           2 : }
     483             : 
     484           2 : func emptyWithBounds(i iterator, start, end *iterator) bool {
     485           2 :         // If i.r is nil, the iterator was constructed from an empty btree.
     486           2 :         // If the end bound is before the start bound, the bounds represent an
     487           2 :         // empty slice of the B-Tree.
     488           2 :         return i.r == nil || (start != nil && end != nil && cmpIter(*end, *start) < 0)
     489           2 : }
     490             : 
     491             : // First seeks to the first file in the iterator and returns it.
     492           2 : func (i *LevelIterator) First() *FileMetadata {
     493           2 :         if i.empty() {
     494           2 :                 return nil
     495           2 :         }
     496           2 :         if i.start != nil {
     497           2 :                 i.iter = i.start.clone()
     498           2 :         } else {
     499           2 :                 i.iter.first()
     500           2 :         }
     501           2 :         if !i.iter.valid() {
     502           2 :                 return nil
     503           2 :         }
     504           2 :         return i.skipFilteredForward(i.iter.cur())
     505             : }
     506             : 
     507             : // Last seeks to the last file in the iterator and returns it.
     508           2 : func (i *LevelIterator) Last() *FileMetadata {
     509           2 :         if i.empty() {
     510           2 :                 return nil
     511           2 :         }
     512           2 :         if i.end != nil {
     513           2 :                 i.iter = i.end.clone()
     514           2 :         } else {
     515           2 :                 i.iter.last()
     516           2 :         }
     517           2 :         if !i.iter.valid() {
     518           0 :                 return nil
     519           0 :         }
     520           2 :         return i.skipFilteredBackward(i.iter.cur())
     521             : }
     522             : 
     523             : // Next advances the iterator to the next file and returns it.
     524           2 : func (i *LevelIterator) Next() *FileMetadata {
     525           2 :         if i.iter.r == nil {
     526           0 :                 return nil
     527           0 :         }
     528           2 :         if invariants.Enabled && (i.iter.pos >= i.iter.n.count || (i.end != nil && cmpIter(i.iter, *i.end) > 0)) {
     529           0 :                 panic("pebble: cannot next forward-exhausted iterator")
     530             :         }
     531           2 :         i.iter.next()
     532           2 :         if !i.iter.valid() {
     533           2 :                 return nil
     534           2 :         }
     535           2 :         return i.skipFilteredForward(i.iter.cur())
     536             : }
     537             : 
     538             : // Prev moves the iterator the previous file and returns it.
     539           2 : func (i *LevelIterator) Prev() *FileMetadata {
     540           2 :         if i.iter.r == nil {
     541           2 :                 return nil
     542           2 :         }
     543           2 :         if invariants.Enabled && (i.iter.pos < 0 || (i.start != nil && cmpIter(i.iter, *i.start) < 0)) {
     544           0 :                 panic("pebble: cannot prev backward-exhausted iterator")
     545             :         }
     546           2 :         i.iter.prev()
     547           2 :         if !i.iter.valid() {
     548           2 :                 return nil
     549           2 :         }
     550           2 :         return i.skipFilteredBackward(i.iter.cur())
     551             : }
     552             : 
     553             : // SeekGE seeks to the first file in the iterator's file set with a largest
     554             : // user key greater than or equal to the provided user key. The iterator must
     555             : // have been constructed from L1+ or from a single sublevel of L0, because it
     556             : // requires the underlying files to be sorted by user keys and non-overlapping.
     557           2 : func (i *LevelIterator) SeekGE(cmp Compare, userKey []byte) *FileMetadata {
     558           2 :         if i.iter.r == nil {
     559           2 :                 return nil
     560           2 :         }
     561           2 :         if invariants.Enabled {
     562           2 :                 i.assertNotL0Cmp()
     563           2 :         }
     564           2 :         m := i.seek(func(m *FileMetadata) bool {
     565           2 :                 return cmp(m.Largest.UserKey, userKey) >= 0
     566           2 :         })
     567           2 :         if i.filter != KeyTypePointAndRange && m != nil {
     568           2 :                 b, ok := m.LargestBound(i.filter)
     569           2 :                 if !ok {
     570           2 :                         m = i.Next()
     571           2 :                 } else if c := cmp(b.UserKey, userKey); c < 0 || c == 0 && b.IsExclusiveSentinel() {
     572           2 :                         // This file does not contain any keys of the type ≥ lower. It
     573           2 :                         // should be filtered, even though it does contain point keys.
     574           2 :                         m = i.Next()
     575           2 :                 }
     576             :         }
     577           2 :         return i.skipFilteredForward(m)
     578             : }
     579             : 
     580             : // assertNotL0Cmp verifies that the btree associated with the iterator is
     581             : // ordered by Smallest key (i.e. L1+ or L0 sublevel) and not by LargestSeqNum
     582             : // (L0).
     583           2 : func (i *LevelIterator) assertNotL0Cmp() {
     584           2 :         if reflect.ValueOf(i.iter.cmp).Pointer() == reflect.ValueOf(btreeCmpSeqNum).Pointer() {
     585           0 :                 panic("Seek used with btreeCmpSeqNum")
     586             :         }
     587             : }
     588             : 
     589             : // SeekLT seeks to the last file in the iterator's file set with a smallest user
     590             : // key less than the provided user key. The iterator must have been constructed
     591             : // from L1+ or from a single sublevel of L0, because it requires the underlying
     592             : // files to be sorted by user keys and non-overlapping.
     593           2 : func (i *LevelIterator) SeekLT(cmp Compare, userKey []byte) *FileMetadata {
     594           2 :         if i.iter.r == nil {
     595           2 :                 return nil
     596           2 :         }
     597           2 :         if invariants.Enabled {
     598           2 :                 i.assertNotL0Cmp()
     599           2 :         }
     600           2 :         i.seek(func(m *FileMetadata) bool {
     601           2 :                 return cmp(m.Smallest.UserKey, userKey) >= 0
     602           2 :         })
     603           2 :         m := i.Prev()
     604           2 :         // Although i.Prev() guarantees that the current file contains keys of the
     605           2 :         // relevant type, it doesn't guarantee that the keys of the relevant type
     606           2 :         // are < userKey.
     607           2 :         if i.filter != KeyTypePointAndRange && m != nil {
     608           2 :                 b, ok := m.SmallestBound(i.filter)
     609           2 :                 if !ok {
     610           0 :                         panic("unreachable")
     611             :                 }
     612           2 :                 if c := cmp(b.UserKey, userKey); c >= 0 {
     613           2 :                         // This file does not contain any keys of the type ≥ lower. It
     614           2 :                         // should be filtered, even though it does contain point keys.
     615           2 :                         m = i.Prev()
     616           2 :                 }
     617             :         }
     618           2 :         return i.skipFilteredBackward(m)
     619             : }
     620             : 
     621             : // skipFilteredForward takes the file metadata at the iterator's current
     622             : // position, and skips forward if the current key-type filter (i.filter)
     623             : // excludes the file. It skips until it finds an unfiltered file or exhausts the
     624             : // level. If lower is != nil, skipFilteredForward skips any files that do not
     625             : // contain keys with the provided key-type ≥ lower.
     626             : //
     627             : // skipFilteredForward also enforces the upper bound, returning nil if at any
     628             : // point the upper bound is exceeded.
     629           2 : func (i *LevelIterator) skipFilteredForward(meta *FileMetadata) *FileMetadata {
     630           2 :         for meta != nil && !meta.ContainsKeyType(i.filter) {
     631           2 :                 i.iter.next()
     632           2 :                 if !i.iter.valid() {
     633           2 :                         meta = nil
     634           2 :                 } else {
     635           2 :                         meta = i.iter.cur()
     636           2 :                 }
     637             :         }
     638           2 :         if meta != nil && i.end != nil && cmpIter(i.iter, *i.end) > 0 {
     639           2 :                 // Exceeded upper bound.
     640           2 :                 meta = nil
     641           2 :         }
     642           2 :         return meta
     643             : }
     644             : 
     645             : // skipFilteredBackward takes the file metadata at the iterator's current
     646             : // position, and skips backward if the current key-type filter (i.filter)
     647             : // excludes the file. It skips until it finds an unfiltered file or exhausts the
     648             : // level. If upper is != nil, skipFilteredBackward skips any files that do not
     649             : // contain keys with the provided key-type < upper.
     650             : //
     651             : // skipFilteredBackward also enforces the lower bound, returning nil if at any
     652             : // point the lower bound is exceeded.
     653           2 : func (i *LevelIterator) skipFilteredBackward(meta *FileMetadata) *FileMetadata {
     654           2 :         for meta != nil && !meta.ContainsKeyType(i.filter) {
     655           2 :                 i.iter.prev()
     656           2 :                 if !i.iter.valid() {
     657           2 :                         meta = nil
     658           2 :                 } else {
     659           2 :                         meta = i.iter.cur()
     660           2 :                 }
     661             :         }
     662           2 :         if meta != nil && i.start != nil && cmpIter(i.iter, *i.start) < 0 {
     663           1 :                 // Exceeded lower bound.
     664           1 :                 meta = nil
     665           1 :         }
     666           2 :         return meta
     667             : }
     668             : 
     669           2 : func (i *LevelIterator) seek(fn func(*FileMetadata) bool) *FileMetadata {
     670           2 :         i.iter.seek(fn)
     671           2 : 
     672           2 :         // i.iter.seek seeked in the unbounded underlying B-Tree. If the iterator
     673           2 :         // has start or end bounds, we may have exceeded them. Reset to the bounds
     674           2 :         // if necessary.
     675           2 :         //
     676           2 :         // NB: The LevelIterator and LevelSlice semantics require that a bounded
     677           2 :         // LevelIterator/LevelSlice containing files x0, x1, ..., xn behave
     678           2 :         // identically to an unbounded LevelIterator/LevelSlice of a B-Tree
     679           2 :         // containing x0, x1, ..., xn. In other words, any files outside the
     680           2 :         // LevelIterator's bounds should not influence the iterator's behavior.
     681           2 :         // When seeking, this means a SeekGE that seeks beyond the end bound,
     682           2 :         // followed by a Prev should return the last element within bounds.
     683           2 :         if i.end != nil && cmpIter(i.iter, *i.end) > 0 {
     684           2 :                 i.iter = i.end.clone()
     685           2 :                 // Since seek(fn) positioned beyond i.end, we know there is nothing to
     686           2 :                 // return within bounds.
     687           2 :                 i.iter.next()
     688           2 :                 return nil
     689           2 :         } else if i.start != nil && cmpIter(i.iter, *i.start) < 0 {
     690           2 :                 i.iter = i.start.clone()
     691           2 :         }
     692           2 :         if !i.iter.valid() {
     693           2 :                 return nil
     694           2 :         }
     695           2 :         return i.iter.cur()
     696             : }
     697             : 
     698             : // Take constructs a LevelFile containing the file at the iterator's current
     699             : // position. Take panics if the iterator is not currently positioned over a
     700             : // file.
     701           2 : func (i *LevelIterator) Take() LevelFile {
     702           2 :         m := i.Current()
     703           2 :         if m == nil {
     704           0 :                 panic("Take called on invalid LevelIterator")
     705             :         }
     706             :         // LevelSlice's start and end fields are immutable and are positioned to
     707             :         // the same position for a LevelFile because they're inclusive, so we can
     708             :         // share one iterator stack between the two bounds.
     709           2 :         boundsIter := i.iter.clone()
     710           2 :         s := newBoundedLevelSlice(i.iter.clone(), &boundsIter, &boundsIter)
     711           2 :         return LevelFile{
     712           2 :                 FileMetadata: m,
     713           2 :                 slice:        s,
     714           2 :         }
     715             : }

Generated by: LCOV version 1.14