LCOV - code coverage report
Current view: top level - pebble/internal/keyspan - interleaving_iter.go (source / functions) Hit Total Coverage
Test: 2024-03-06 08:16Z f3e3d4aa - tests only.lcov Lines: 606 654 92.7 %
Date: 2024-03-06 08:16:53 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2021 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 keyspan
       6             : 
       7             : import (
       8             :         "context"
       9             :         "fmt"
      10             : 
      11             :         "github.com/cockroachdb/errors"
      12             :         "github.com/cockroachdb/pebble/internal/base"
      13             :         "github.com/cockroachdb/pebble/internal/invariants"
      14             : )
      15             : 
      16             : // A SpanMask may be used to configure an interleaving iterator to skip point
      17             : // keys that fall within the bounds of some spans.
      18             : type SpanMask interface {
      19             :         // SpanChanged is invoked by an interleaving iterator whenever the current
      20             :         // span changes. As the iterator passes into or out of a Span, it invokes
      21             :         // SpanChanged, passing the new Span. When the iterator passes out of a
      22             :         // span's boundaries and is no longer covered by any span, SpanChanged is
      23             :         // invoked with a nil span.
      24             :         //
      25             :         // SpanChanged is invoked before SkipPoint, and callers may use SpanChanged
      26             :         // to recalculate state used by SkipPoint for masking.
      27             :         //
      28             :         // SpanChanged may be invoked consecutively with identical spans under some
      29             :         // circumstances, such as repeatedly absolutely positioning an iterator to
      30             :         // positions covered by the same span, or while changing directions.
      31             :         SpanChanged(*Span)
      32             :         // SkipPoint is invoked by the interleaving iterator whenever the iterator
      33             :         // encounters a point key covered by a Span. If SkipPoint returns true, the
      34             :         // interleaving iterator skips the point key and all larger keys with the
      35             :         // same prefix. This is used during range key iteration to skip over point
      36             :         // keys 'masked' by range keys.
      37             :         SkipPoint(userKey []byte) bool
      38             : }
      39             : 
      40             : // InterleavingIter combines an iterator over point keys with an iterator over
      41             : // key spans.
      42             : //
      43             : // Throughout Pebble, some keys apply at single discrete points within the user
      44             : // keyspace. Other keys apply over continuous spans of the user key space.
      45             : // Internally, iterators over point keys adhere to the base.InternalIterator
      46             : // interface, and iterators over spans adhere to the keyspan.FragmentIterator
      47             : // interface. The InterleavingIterator wraps a point iterator and span iterator,
      48             : // providing access to all the elements of both iterators.
      49             : //
      50             : // The InterleavingIterator implements the point base.InternalIterator
      51             : // interface. After any of the iterator's methods return a key, a caller may
      52             : // call Span to retrieve the span covering the returned key, if any.  A span is
      53             : // considered to 'cover' a returned key if the span's [start, end) bounds
      54             : // include the key's user key.
      55             : //
      56             : // In addition to tracking the current covering span, InterleavingIter returns a
      57             : // special InternalKey at span start boundaries. Start boundaries are surfaced
      58             : // as a synthetic span marker: an InternalKey with the boundary as the user key,
      59             : // the infinite sequence number and a key kind selected from an arbitrary key
      60             : // the infinite sequence number and an arbitrary contained key's kind. Since
      61             : // which of the Span's key's kind is surfaced is undefined, the caller should
      62             : // not use the InternalKey's kind. The caller should only rely on the `Span`
      63             : // method for retrieving information about spanning keys. The interleaved
      64             : // synthetic keys have the infinite sequence number so that they're interleaved
      65             : // before any point keys with the same user key when iterating forward and after
      66             : // when iterating backward.
      67             : //
      68             : // Interleaving the synthetic start key boundaries at the maximum sequence
      69             : // number provides an opportunity for the higher-level, public Iterator to
      70             : // observe the Span, even if no live points keys exist within the boudns of the
      71             : // Span.
      72             : //
      73             : // When returning a synthetic marker key for a start boundary, InterleavingIter
      74             : // will truncate the span's start bound to the SeekGE or SeekPrefixGE search
      75             : // key. For example, a SeekGE("d") that finds a span [a, z) may return a
      76             : // synthetic span marker key `d#72057594037927935,21`.
      77             : //
      78             : // If bounds have been applied to the iterator through SetBounds,
      79             : // InterleavingIter will truncate the bounds of spans returned through Span to
      80             : // the set bounds. The bounds returned through Span are not truncated by a
      81             : // SeekGE or SeekPrefixGE search key. Consider, for example SetBounds('c', 'e'),
      82             : // with an iterator containing the Span [a,z):
      83             : //
      84             : //      First()     = `c#72057594037927935,21`        Span() = [c,e)
      85             : //      SeekGE('d') = `d#72057594037927935,21`        Span() = [c,e)
      86             : //
      87             : // InterleavedIter does not interleave synthetic markers for spans that do not
      88             : // contain any keys.
      89             : //
      90             : // # SpanMask
      91             : //
      92             : // InterelavingIter takes a SpanMask parameter that may be used to configure the
      93             : // behavior of the iterator. See the documentation on the SpanMask type.
      94             : //
      95             : // All spans containing keys are exposed during iteration.
      96             : type InterleavingIter struct {
      97             :         cmp         base.Compare
      98             :         comparer    *base.Comparer
      99             :         pointIter   base.InternalIterator
     100             :         keyspanIter FragmentIterator
     101             :         mask        SpanMask
     102             : 
     103             :         // lower and upper hold the iteration bounds set through SetBounds.
     104             :         lower, upper []byte
     105             :         // keyBuf is used to copy SeekGE or SeekPrefixGE arguments when they're used
     106             :         // to truncate a span. The byte slices backing a SeekGE/SeekPrefixGE search
     107             :         // keys can come directly from the end user, so they're copied into keyBuf
     108             :         // to ensure key stability.
     109             :         keyBuf []byte
     110             :         // nextPrefixBuf is used during SeekPrefixGE calls to store the truncated
     111             :         // upper bound of the returned spans. SeekPrefixGE truncates the returned
     112             :         // spans to an upper bound of the seeked prefix's immediate successor.
     113             :         nextPrefixBuf []byte
     114             :         pointKey      *base.InternalKey
     115             :         pointVal      base.LazyValue
     116             :         // err holds an iterator error from either pointIter or keyspanIter. It's
     117             :         // reset to nil on seeks. An overview of error-handling mechanics:
     118             :         //
     119             :         // Whenever either pointIter or keyspanIter is respositioned and a nil
     120             :         // key/span is returned, the code performing the positioning is responsible
     121             :         // for checking the iterator's Error() value. This happens in savePoint and
     122             :         // saveSpan[Forward,Backward].
     123             :         //
     124             :         // Once i.err is non-nil, the computation of i.pos must set i.pos =
     125             :         // posExhausted. This happens in compute[Smallest|Largest]Pos and
     126             :         // [next|prev]Pos. Setting i.pos to posExhausted ensures we'll yield nil to
     127             :         // the caller, which they'll interpret as a signal they must check Error().
     128             :         //
     129             :         // INVARIANTS:
     130             :         // i.err != nil => i.pos = posExhausted
     131             :         err error
     132             :         // prefix records the iterator's current prefix if the iterator is in prefix
     133             :         // mode. During prefix mode, Pebble will truncate spans to the next prefix.
     134             :         // If the iterator subsequently leaves prefix mode, the existing span cached
     135             :         // in i.span must be invalidated because its bounds do not reflect the
     136             :         // original span's true bounds.
     137             :         prefix []byte
     138             :         // span holds the span at the keyspanIter's current position. If the span is
     139             :         // wholly contained within the iterator bounds, this span is directly
     140             :         // returned to the iterator consumer through Span(). If either bound needed
     141             :         // to be truncated to the iterator bounds, then truncated is set to true and
     142             :         // Span() must return a pointer to truncatedSpan.
     143             :         span *Span
     144             :         // spanMarker holds the synthetic key that is returned when the iterator
     145             :         // passes over a key span's start bound.
     146             :         spanMarker base.InternalKey
     147             :         // truncated indicates whether or not the span at the current position
     148             :         // needed to be truncated. If it did, truncatedSpan holds the truncated
     149             :         // span that should be returned.
     150             :         truncatedSpan Span
     151             :         truncated     bool
     152             : 
     153             :         // Keeping all of the bools/uint8s together reduces the sizeof the struct.
     154             : 
     155             :         // pos encodes the current position of the iterator: exhausted, on the point
     156             :         // key, on a keyspan start, or on a keyspan end.
     157             :         pos interleavePos
     158             :         // withinSpan indicates whether the iterator is currently positioned within
     159             :         // the bounds of the current span (i.span). withinSpan must be updated
     160             :         // whenever the interleaving iterator's position enters or exits the bounds
     161             :         // of a span.
     162             :         withinSpan bool
     163             :         // spanMarkerTruncated is set by SeekGE/SeekPrefixGE calls that truncate a
     164             :         // span's start bound marker to the search key. It's returned to false on
     165             :         // the next repositioning of the keyspan iterator.
     166             :         spanMarkerTruncated bool
     167             :         // maskSpanChangedCalled records whether or not the last call to
     168             :         // SpanMask.SpanChanged provided the current span (i.span) or not.
     169             :         maskSpanChangedCalled bool
     170             :         // dir indicates the direction of iteration: forward (+1) or backward (-1)
     171             :         dir int8
     172             : }
     173             : 
     174             : // interleavePos indicates the iterator's current position. Note that both
     175             : // keyspanStart and keyspanEnd positions correspond to their user key boundaries
     176             : // with maximal sequence numbers. This means in the forward direction
     177             : // posKeyspanStart and posKeyspanEnd are always interleaved before a posPointKey
     178             : // with the same user key.
     179             : type interleavePos int8
     180             : 
     181             : const (
     182             :         posUninitialized interleavePos = iota
     183             :         posExhausted
     184             :         posPointKey
     185             :         posKeyspanStart
     186             :         posKeyspanEnd
     187             : )
     188             : 
     189             : // Assert that *InterleavingIter implements the InternalIterator interface.
     190             : var _ base.InternalIterator = &InterleavingIter{}
     191             : 
     192             : // InterleavingIterOpts holds options configuring the behavior of a
     193             : // InterleavingIter.
     194             : type InterleavingIterOpts struct {
     195             :         Mask                   SpanMask
     196             :         LowerBound, UpperBound []byte
     197             : }
     198             : 
     199             : // Init initializes the InterleavingIter to interleave point keys from pointIter
     200             : // with key spans from keyspanIter.
     201             : //
     202             : // The point iterator must already have the bounds provided on opts. Init does
     203             : // not propagate the bounds down the iterator stack.
     204             : func (i *InterleavingIter) Init(
     205             :         comparer *base.Comparer,
     206             :         pointIter base.InternalIterator,
     207             :         keyspanIter FragmentIterator,
     208             :         opts InterleavingIterOpts,
     209           1 : ) {
     210           1 :         keyspanIter = MaybeAssert(keyspanIter, comparer.Compare)
     211           1 :         // To debug:
     212           1 :         // keyspanIter = InjectLogging(keyspanIter, base.DefaultLogger)
     213           1 :         *i = InterleavingIter{
     214           1 :                 cmp:         comparer.Compare,
     215           1 :                 comparer:    comparer,
     216           1 :                 pointIter:   pointIter,
     217           1 :                 keyspanIter: keyspanIter,
     218           1 :                 mask:        opts.Mask,
     219           1 :                 lower:       opts.LowerBound,
     220           1 :                 upper:       opts.UpperBound,
     221           1 :         }
     222           1 : }
     223             : 
     224             : // InitSeekGE may be called after Init but before any positioning method.
     225             : // InitSeekGE initializes the current position of the point iterator and then
     226             : // performs a SeekGE on the keyspan iterator using the provided key. InitSeekGE
     227             : // returns whichever point or keyspan key is smaller. After InitSeekGE, the
     228             : // iterator is positioned and may be repositioned using relative positioning
     229             : // methods.
     230             : //
     231             : // This method is used specifically for lazily constructing combined iterators.
     232             : // It allows for seeding the iterator with the current position of the point
     233             : // iterator.
     234             : func (i *InterleavingIter) InitSeekGE(
     235             :         prefix, key []byte, pointKey *base.InternalKey, pointValue base.LazyValue,
     236           1 : ) (*base.InternalKey, base.LazyValue) {
     237           1 :         i.dir = +1
     238           1 :         i.clearMask()
     239           1 :         i.prefix = prefix
     240           1 :         i.savePoint(pointKey, pointValue)
     241           1 :         // NB: This keyspanSeekGE call will truncate the span to the seek key if
     242           1 :         // necessary. This truncation is important for cases where a switch to
     243           1 :         // combined iteration is made during a user-initiated SeekGE.
     244           1 :         i.keyspanSeekGE(key, prefix)
     245           1 :         i.computeSmallestPos()
     246           1 :         return i.yieldPosition(key, i.nextPos)
     247           1 : }
     248             : 
     249             : // InitSeekLT may be called after Init but before any positioning method.
     250             : // InitSeekLT initializes the current position of the point iterator and then
     251             : // performs a SeekLT on the keyspan iterator using the provided key. InitSeekLT
     252             : // returns whichever point or keyspan key is larger. After InitSeekLT, the
     253             : // iterator is positioned and may be repositioned using relative positioning
     254             : // methods.
     255             : //
     256             : // This method is used specifically for lazily constructing combined iterators.
     257             : // It allows for seeding the iterator with the current position of the point
     258             : // iterator.
     259             : func (i *InterleavingIter) InitSeekLT(
     260             :         key []byte, pointKey *base.InternalKey, pointValue base.LazyValue,
     261           1 : ) (*base.InternalKey, base.LazyValue) {
     262           1 :         i.dir = -1
     263           1 :         i.clearMask()
     264           1 :         i.savePoint(pointKey, pointValue)
     265           1 :         i.keyspanSeekLT(key)
     266           1 :         i.computeLargestPos()
     267           1 :         return i.yieldPosition(i.lower, i.prevPos)
     268           1 : }
     269             : 
     270             : // SeekGE implements (base.InternalIterator).SeekGE.
     271             : //
     272             : // If there exists a span with a start key ≤ the first matching point key,
     273             : // SeekGE will return a synthetic span marker key for the span. If this span's
     274             : // start key is less than key, the returned marker will be truncated to key.
     275             : // Note that this search-key truncation of the marker's key is not applied to
     276             : // the span returned by Span.
     277             : //
     278             : // NB: In accordance with the base.InternalIterator contract:
     279             : //
     280             : //      i.lower ≤ key
     281             : func (i *InterleavingIter) SeekGE(
     282             :         key []byte, flags base.SeekGEFlags,
     283           1 : ) (*base.InternalKey, base.LazyValue) {
     284           1 :         i.err = nil
     285           1 :         i.clearMask()
     286           1 :         i.disablePrefixMode()
     287           1 :         i.savePoint(i.pointIter.SeekGE(key, flags))
     288           1 : 
     289           1 :         // We need to seek the keyspan iterator too. If the keyspan iterator was
     290           1 :         // already positioned at a span, we might be able to avoid the seek if the
     291           1 :         // seek key falls within the existing span's bounds.
     292           1 :         if i.span != nil && i.cmp(key, i.span.End) < 0 && i.cmp(key, i.span.Start) >= 0 {
     293           1 :                 // We're seeking within the existing span's bounds. We still might need
     294           1 :                 // truncate the span to the iterator's bounds.
     295           1 :                 i.saveSpanForward(i.span, nil)
     296           1 :                 i.savedKeyspan()
     297           1 :         } else {
     298           1 :                 i.keyspanSeekGE(key, nil /* prefix */)
     299           1 :         }
     300             : 
     301           1 :         i.dir = +1
     302           1 :         i.computeSmallestPos()
     303           1 :         return i.yieldPosition(key, i.nextPos)
     304             : }
     305             : 
     306             : // SeekPrefixGE implements (base.InternalIterator).SeekPrefixGE.
     307             : //
     308             : // If there exists a span with a start key ≤ the first matching point key,
     309             : // SeekPrefixGE will return a synthetic span marker key for the span. If this
     310             : // span's start key is less than key, the returned marker will be truncated to
     311             : // key. Note that this search-key truncation of the marker's key is not applied
     312             : // to the span returned by Span.
     313             : //
     314             : // NB: In accordance with the base.InternalIterator contract:
     315             : //
     316             : //      i.lower ≤ key
     317             : func (i *InterleavingIter) SeekPrefixGE(
     318             :         prefix, key []byte, flags base.SeekGEFlags,
     319           1 : ) (*base.InternalKey, base.LazyValue) {
     320           1 :         i.err = nil
     321           1 :         i.clearMask()
     322           1 :         i.prefix = prefix
     323           1 :         i.savePoint(i.pointIter.SeekPrefixGE(prefix, key, flags))
     324           1 : 
     325           1 :         // We need to seek the keyspan iterator too. If the keyspan iterator was
     326           1 :         // already positioned at a span, we might be able to avoid the seek if the
     327           1 :         // entire seek prefix key falls within the existing span's bounds.
     328           1 :         //
     329           1 :         // During a SeekPrefixGE, Pebble defragments range keys within the bounds of
     330           1 :         // the prefix. For example, a SeekPrefixGE('c', 'c@8') must defragment the
     331           1 :         // any overlapping range keys within the bounds of [c,c\00).
     332           1 :         //
     333           1 :         // If range keys are fragmented within a prefix (eg, because a version
     334           1 :         // within a prefix was chosen as an sstable boundary), then it's possible
     335           1 :         // the seek key falls into the current i.span, but the current i.span does
     336           1 :         // not wholly cover the seek prefix.
     337           1 :         //
     338           1 :         // For example, a SeekPrefixGE('d@5') may only defragment a range key to
     339           1 :         // the bounds of [c@2,e). A subsequent SeekPrefixGE('c@0') must re-seek the
     340           1 :         // keyspan iterator, because although 'c@0' is contained within [c@2,e), the
     341           1 :         // full span of the prefix is not.
     342           1 :         //
     343           1 :         // Similarly, a SeekPrefixGE('a@3') may only defragment a range key to the
     344           1 :         // bounds [a,c@8). A subsequent SeekPrefixGE('c@10') must re-seek the
     345           1 :         // keyspan iterator, because although 'c@10' is contained within [a,c@8),
     346           1 :         // the full span of the prefix is not.
     347           1 :         seekKeyspanIter := true
     348           1 :         if i.span != nil && i.cmp(prefix, i.span.Start) >= 0 {
     349           1 :                 if ei := i.comparer.Split(i.span.End); i.cmp(prefix, i.span.End[:ei]) < 0 {
     350           1 :                         // We're seeking within the existing span's bounds. We still might need
     351           1 :                         // truncate the span to the iterator's bounds.
     352           1 :                         i.saveSpanForward(i.span, nil)
     353           1 :                         i.savedKeyspan()
     354           1 :                         seekKeyspanIter = false
     355           1 :                 }
     356             :         }
     357           1 :         if seekKeyspanIter {
     358           1 :                 i.keyspanSeekGE(key, prefix)
     359           1 :         }
     360             : 
     361           1 :         i.dir = +1
     362           1 :         i.computeSmallestPos()
     363           1 :         return i.yieldPosition(key, i.nextPos)
     364             : }
     365             : 
     366             : // SeekLT implements (base.InternalIterator).SeekLT.
     367             : func (i *InterleavingIter) SeekLT(
     368             :         key []byte, flags base.SeekLTFlags,
     369           1 : ) (*base.InternalKey, base.LazyValue) {
     370           1 :         i.err = nil
     371           1 :         i.clearMask()
     372           1 :         i.disablePrefixMode()
     373           1 :         i.savePoint(i.pointIter.SeekLT(key, flags))
     374           1 : 
     375           1 :         // We need to seek the keyspan iterator too. If the keyspan iterator was
     376           1 :         // already positioned at a span, we might be able to avoid the seek if the
     377           1 :         // seek key falls within the existing span's bounds.
     378           1 :         if i.span != nil && i.cmp(key, i.span.Start) > 0 && i.cmp(key, i.span.End) < 0 {
     379           1 :                 // We're seeking within the existing span's bounds. We still might need
     380           1 :                 // truncate the span to the iterator's bounds.
     381           1 :                 i.saveSpanBackward(i.span, nil)
     382           1 :                 // The span's start key is still not guaranteed to be less than key,
     383           1 :                 // because of the bounds enforcement. Consider the following example:
     384           1 :                 //
     385           1 :                 // Bounds are set to [d,e). The user performs a SeekLT(d). The
     386           1 :                 // FragmentIterator.SeekLT lands on a span [b,f). This span has a start
     387           1 :                 // key less than d, as expected. Above, saveSpanBackward truncates the
     388           1 :                 // span to match the iterator's current bounds, modifying the span to
     389           1 :                 // [d,e), which does not overlap the search space of [-∞, d).
     390           1 :                 //
     391           1 :                 // This problem is a consequence of the SeekLT's exclusive search key
     392           1 :                 // and the fact that we don't perform bounds truncation at every leaf
     393           1 :                 // iterator.
     394           1 :                 if i.span != nil && i.truncated && i.cmp(i.truncatedSpan.Start, key) >= 0 {
     395           1 :                         i.span = nil
     396           1 :                 }
     397           1 :                 i.savedKeyspan()
     398           1 :         } else {
     399           1 :                 i.keyspanSeekLT(key)
     400           1 :         }
     401             : 
     402           1 :         i.dir = -1
     403           1 :         i.computeLargestPos()
     404           1 :         return i.yieldPosition(i.lower, i.prevPos)
     405             : }
     406             : 
     407             : // First implements (base.InternalIterator).First.
     408           1 : func (i *InterleavingIter) First() (*base.InternalKey, base.LazyValue) {
     409           1 :         i.err = nil
     410           1 :         i.clearMask()
     411           1 :         i.disablePrefixMode()
     412           1 :         i.savePoint(i.pointIter.First())
     413           1 :         i.saveSpanForward(i.keyspanIter.First())
     414           1 :         i.savedKeyspan()
     415           1 :         i.dir = +1
     416           1 :         i.computeSmallestPos()
     417           1 :         return i.yieldPosition(i.lower, i.nextPos)
     418           1 : }
     419             : 
     420             : // Last implements (base.InternalIterator).Last.
     421           1 : func (i *InterleavingIter) Last() (*base.InternalKey, base.LazyValue) {
     422           1 :         i.err = nil
     423           1 :         i.clearMask()
     424           1 :         i.disablePrefixMode()
     425           1 :         i.savePoint(i.pointIter.Last())
     426           1 :         i.saveSpanBackward(i.keyspanIter.Last())
     427           1 :         i.savedKeyspan()
     428           1 :         i.dir = -1
     429           1 :         i.computeLargestPos()
     430           1 :         return i.yieldPosition(i.lower, i.prevPos)
     431           1 : }
     432             : 
     433             : // Next implements (base.InternalIterator).Next.
     434           1 : func (i *InterleavingIter) Next() (*base.InternalKey, base.LazyValue) {
     435           1 :         if i.dir == -1 {
     436           1 :                 // Switching directions.
     437           1 :                 i.dir = +1
     438           1 : 
     439           1 :                 if i.mask != nil {
     440           1 :                         // Clear the mask while we reposition the point iterator. While
     441           1 :                         // switching directions, we may move the point iterator outside of
     442           1 :                         // i.span's bounds.
     443           1 :                         i.clearMask()
     444           1 :                 }
     445             : 
     446             :                 // When switching directions, iterator state corresponding to the
     447             :                 // current iterator position (as indicated by i.pos) is already correct.
     448             :                 // However any state that has yet to be interleaved describes a position
     449             :                 // behind the current iterator position and needs to be updated to
     450             :                 // describe the position ahead of the current iterator position.
     451           1 :                 switch i.pos {
     452           0 :                 case posExhausted:
     453             :                         // Nothing to do. The below nextPos call will move both the point
     454             :                         // key and span to their next positions and return
     455             :                         // MIN(point,s.Start).
     456           1 :                 case posPointKey:
     457           1 :                         // If we're currently on a point key, the below nextPos will
     458           1 :                         // correctly Next the point key iterator to the next point key.
     459           1 :                         // Do we need to move the span forwards? If the current span lies
     460           1 :                         // entirely behind the current key (!i.withinSpan), then we
     461           1 :                         // need to move it to the first span in the forward direction.
     462           1 :                         if !i.withinSpan {
     463           1 :                                 i.saveSpanForward(i.keyspanIter.Next())
     464           1 :                                 i.savedKeyspan()
     465           1 :                         }
     466           1 :                 case posKeyspanStart:
     467           1 :                         i.withinSpan = true
     468           1 :                         // Since we're positioned on a Span, the pointIter is positioned
     469           1 :                         // entirely behind the current iterator position. Reposition it
     470           1 :                         // ahead of the current iterator position.
     471           1 :                         i.savePoint(i.pointIter.Next())
     472           0 :                 case posKeyspanEnd:
     473           0 :                         // Since we're positioned on a Span, the pointIter is positioned
     474           0 :                         // entirely behind of the current iterator position. Reposition it
     475           0 :                         // ahead the current iterator position.
     476           0 :                         i.savePoint(i.pointIter.Next())
     477             :                 }
     478             :                 // Fallthrough to calling i.nextPos.
     479             :         }
     480           1 :         i.nextPos()
     481           1 :         return i.yieldPosition(i.lower, i.nextPos)
     482             : }
     483             : 
     484             : // NextPrefix implements (base.InternalIterator).NextPrefix.
     485           1 : func (i *InterleavingIter) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
     486           1 :         if i.dir == -1 {
     487           0 :                 panic("pebble: cannot switch directions with NextPrefix")
     488             :         }
     489             : 
     490           1 :         switch i.pos {
     491           1 :         case posExhausted:
     492           1 :                 return nil, base.LazyValue{}
     493           1 :         case posPointKey:
     494           1 :                 i.savePoint(i.pointIter.NextPrefix(succKey))
     495           1 :                 if i.withinSpan {
     496           1 :                         if i.pointKey == nil || i.cmp(i.span.End, i.pointKey.UserKey) <= 0 {
     497           1 :                                 i.pos = posKeyspanEnd
     498           1 :                         } else {
     499           1 :                                 i.pos = posPointKey
     500           1 :                         }
     501           1 :                 } else {
     502           1 :                         i.computeSmallestPos()
     503           1 :                 }
     504           1 :         case posKeyspanStart, posKeyspanEnd:
     505           1 :                 i.nextPos()
     506             :         }
     507           1 :         return i.yieldPosition(i.lower, i.nextPos)
     508             : }
     509             : 
     510             : // Prev implements (base.InternalIterator).Prev.
     511           1 : func (i *InterleavingIter) Prev() (*base.InternalKey, base.LazyValue) {
     512           1 :         if i.dir == +1 {
     513           1 :                 // Switching directions.
     514           1 :                 i.dir = -1
     515           1 : 
     516           1 :                 if i.mask != nil {
     517           1 :                         // Clear the mask while we reposition the point iterator. While
     518           1 :                         // switching directions, we may move the point iterator outside of
     519           1 :                         // i.span's bounds.
     520           1 :                         i.clearMask()
     521           1 :                 }
     522             : 
     523             :                 // When switching directions, iterator state corresponding to the
     524             :                 // current iterator position (as indicated by i.pos) is already correct.
     525             :                 // However any state that has yet to be interleaved describes a position
     526             :                 // ahead of the current iterator position and needs to be updated to
     527             :                 // describe the position behind the current iterator position.
     528           1 :                 switch i.pos {
     529           0 :                 case posExhausted:
     530             :                         // Nothing to do. The below prevPos call will move both the point
     531             :                         // key and span to previous positions and return MAX(point, s.End).
     532           1 :                 case posPointKey:
     533           1 :                         // If we're currently on a point key, the point iterator is in the
     534           1 :                         // right place and the call to prevPos will correctly Prev the point
     535           1 :                         // key iterator to the previous point key. Do we need to move the
     536           1 :                         // span backwards? If the current span lies entirely ahead of the
     537           1 :                         // current key (!i.withinSpan), then we need to move it to the first
     538           1 :                         // span in the reverse direction.
     539           1 :                         if !i.withinSpan {
     540           1 :                                 i.saveSpanBackward(i.keyspanIter.Prev())
     541           1 :                                 i.savedKeyspan()
     542           1 :                         }
     543           1 :                 case posKeyspanStart:
     544           1 :                         // Since we're positioned on a Span, the pointIter is positioned
     545           1 :                         // entirely ahead of the current iterator position. Reposition it
     546           1 :                         // behind the current iterator position.
     547           1 :                         i.savePoint(i.pointIter.Prev())
     548           1 :                         // Without considering truncation of spans to seek keys, the keyspan
     549           1 :                         // iterator is already in the right place. But consider span [a, z)
     550           1 :                         // and this sequence of iterator calls:
     551           1 :                         //
     552           1 :                         //   SeekGE('c') = c.RANGEKEYSET#72057594037927935
     553           1 :                         //   Prev()      = a.RANGEKEYSET#72057594037927935
     554           1 :                         //
     555           1 :                         // If the current span's start key was last surfaced truncated due
     556           1 :                         // to a SeekGE or SeekPrefixGE call, then it's still relevant in the
     557           1 :                         // reverse direction with an untruncated start key.
     558           1 :                         if i.spanMarkerTruncated {
     559           1 :                                 // When we fallthrough to calling prevPos, we want to move to
     560           1 :                                 // MAX(point, span.Start). We cheat here by claiming we're
     561           1 :                                 // currently on the end boundary, so that we'll move on to the
     562           1 :                                 // untruncated start key if necessary.
     563           1 :                                 i.pos = posKeyspanEnd
     564           1 :                         }
     565           0 :                 case posKeyspanEnd:
     566           0 :                         // Since we're positioned on a Span, the pointIter is positioned
     567           0 :                         // entirely ahead of the current iterator position. Reposition it
     568           0 :                         // behind the current iterator position.
     569           0 :                         i.savePoint(i.pointIter.Prev())
     570             :                 }
     571             : 
     572           1 :                 if i.spanMarkerTruncated {
     573           1 :                         // Save the keyspan again to clear truncation.
     574           1 :                         i.savedKeyspan()
     575           1 :                 }
     576             :                 // Fallthrough to calling i.prevPos.
     577             :         }
     578           1 :         i.prevPos()
     579           1 :         return i.yieldPosition(i.lower, i.prevPos)
     580             : }
     581             : 
     582             : // computeSmallestPos sets i.{pos,withinSpan} to:
     583             : //
     584             : //      MIN(i.pointKey, i.span.Start)
     585           1 : func (i *InterleavingIter) computeSmallestPos() {
     586           1 :         if i.err == nil {
     587           1 :                 if i.span != nil && (i.pointKey == nil || i.cmp(i.startKey(), i.pointKey.UserKey) <= 0) {
     588           1 :                         i.withinSpan = true
     589           1 :                         i.pos = posKeyspanStart
     590           1 :                         return
     591           1 :                 }
     592           1 :                 i.withinSpan = false
     593           1 :                 if i.pointKey != nil {
     594           1 :                         i.pos = posPointKey
     595           1 :                         return
     596           1 :                 }
     597             :         }
     598           1 :         i.pos = posExhausted
     599             : }
     600             : 
     601             : // computeLargestPos sets i.{pos,withinSpan} to:
     602             : //
     603             : //      MAX(i.pointKey, i.span.End)
     604           1 : func (i *InterleavingIter) computeLargestPos() {
     605           1 :         if i.err == nil {
     606           1 :                 if i.span != nil && (i.pointKey == nil || i.cmp(i.span.End, i.pointKey.UserKey) > 0) {
     607           1 :                         i.withinSpan = true
     608           1 :                         i.pos = posKeyspanEnd
     609           1 :                         return
     610           1 :                 }
     611           1 :                 i.withinSpan = false
     612           1 :                 if i.pointKey != nil {
     613           1 :                         i.pos = posPointKey
     614           1 :                         return
     615           1 :                 }
     616             :         }
     617           1 :         i.pos = posExhausted
     618             : }
     619             : 
     620             : // nextPos advances the iterator one position in the forward direction.
     621           1 : func (i *InterleavingIter) nextPos() {
     622           1 :         if invariants.Enabled {
     623           1 :                 defer func() {
     624           1 :                         if i.err != nil && i.pos != posExhausted {
     625           0 :                                 panic(errors.AssertionFailedf("iterator has accumulated error but i.pos = %d", i.pos))
     626             :                         }
     627             :                 }()
     628             :         }
     629             :         // NB: If i.err != nil or any of the positioning methods performed in this
     630             :         // function result in i.err != nil, we must set i.pos = posExhausted. We
     631             :         // perform this check explicitly here, but if any of the branches below
     632             :         // advance either iterator, they must also check i.err and set posExhausted
     633             :         // if necessary.
     634           1 :         if i.err != nil {
     635           1 :                 i.pos = posExhausted
     636           1 :                 return
     637           1 :         }
     638             : 
     639           1 :         switch i.pos {
     640           1 :         case posExhausted:
     641           1 :                 i.savePoint(i.pointIter.Next())
     642           1 :                 i.saveSpanForward(i.keyspanIter.Next())
     643           1 :                 i.savedKeyspan()
     644           1 :                 i.computeSmallestPos()
     645           1 :         case posPointKey:
     646           1 :                 i.savePoint(i.pointIter.Next())
     647           1 :                 if i.err != nil {
     648           1 :                         i.pos = posExhausted
     649           1 :                         return
     650           1 :                 }
     651             :                 // If we're not currently within the span, we want to chose the
     652             :                 // MIN(pointKey,span.Start), which is exactly the calculation performed
     653             :                 // by computeSmallestPos.
     654           1 :                 if !i.withinSpan {
     655           1 :                         i.computeSmallestPos()
     656           1 :                         return
     657           1 :                 }
     658             :                 // i.withinSpan=true
     659             :                 // Since we previously were within the span, we want to choose the
     660             :                 // MIN(pointKey,span.End).
     661           1 :                 switch {
     662           0 :                 case i.span == nil:
     663           0 :                         panic("i.withinSpan=true and i.span=nil")
     664           1 :                 case i.pointKey == nil:
     665           1 :                         // Since i.withinSpan=true, we step onto the end boundary of the
     666           1 :                         // keyspan.
     667           1 :                         i.pos = posKeyspanEnd
     668           1 :                 default:
     669           1 :                         // i.withinSpan && i.pointKey != nil && i.span != nil
     670           1 :                         if i.cmp(i.span.End, i.pointKey.UserKey) <= 0 {
     671           1 :                                 i.pos = posKeyspanEnd
     672           1 :                         } else {
     673           1 :                                 i.pos = posPointKey
     674           1 :                         }
     675             :                 }
     676           1 :         case posKeyspanStart:
     677           1 :                 // Either a point key or the span's end key comes next.
     678           1 :                 if i.pointKey != nil && i.cmp(i.pointKey.UserKey, i.span.End) < 0 {
     679           1 :                         i.pos = posPointKey
     680           1 :                 } else {
     681           1 :                         i.pos = posKeyspanEnd
     682           1 :                 }
     683           1 :         case posKeyspanEnd:
     684           1 :                 i.saveSpanForward(i.keyspanIter.Next())
     685           1 :                 i.savedKeyspan()
     686           1 :                 i.computeSmallestPos()
     687           0 :         default:
     688           0 :                 panic(fmt.Sprintf("unexpected pos=%d", i.pos))
     689             :         }
     690             : }
     691             : 
     692             : // prevPos advances the iterator one position in the reverse direction.
     693           1 : func (i *InterleavingIter) prevPos() {
     694           1 :         if invariants.Enabled {
     695           1 :                 defer func() {
     696           1 :                         if i.err != nil && i.pos != posExhausted {
     697           0 :                                 panic(errors.AssertionFailedf("iterator has accumulated error but i.pos = %d", i.pos))
     698             :                         }
     699             :                 }()
     700             :         }
     701             :         // NB: If i.err != nil or any of the positioning methods performed in this
     702             :         // function result in i.err != nil, we must set i.pos = posExhausted. We
     703             :         // perform this check explicitly here, but if any of the branches below
     704             :         // advance either iterator, they must also check i.err and set posExhausted
     705             :         // if necessary.
     706           1 :         if i.err != nil {
     707           1 :                 i.pos = posExhausted
     708           1 :                 return
     709           1 :         }
     710             : 
     711           1 :         switch i.pos {
     712           1 :         case posExhausted:
     713           1 :                 i.savePoint(i.pointIter.Prev())
     714           1 :                 i.saveSpanBackward(i.keyspanIter.Prev())
     715           1 :                 i.savedKeyspan()
     716           1 :                 i.computeLargestPos()
     717           1 :         case posPointKey:
     718           1 :                 i.savePoint(i.pointIter.Prev())
     719           1 :                 if i.err != nil {
     720           1 :                         i.pos = posExhausted
     721           1 :                         return
     722           1 :                 }
     723             :                 // If we're not currently covered by the span, we want to chose the
     724             :                 // MAX(pointKey,span.End), which is exactly the calculation performed
     725             :                 // by computeLargestPos.
     726           1 :                 if !i.withinSpan {
     727           1 :                         i.computeLargestPos()
     728           1 :                         return
     729           1 :                 }
     730           1 :                 switch {
     731           0 :                 case i.span == nil:
     732           0 :                         panic("withinSpan=true, but i.span == nil")
     733           1 :                 case i.pointKey == nil:
     734           1 :                         i.pos = posKeyspanEnd
     735           1 :                 default:
     736           1 :                         // i.withinSpan && i.pointKey != nil && i.span != nil
     737           1 :                         if i.cmp(i.span.Start, i.pointKey.UserKey) > 0 {
     738           1 :                                 i.pos = posKeyspanStart
     739           1 :                         } else {
     740           1 :                                 i.pos = posPointKey
     741           1 :                         }
     742             :                 }
     743           1 :         case posKeyspanStart:
     744           1 :                 i.saveSpanBackward(i.keyspanIter.Prev())
     745           1 :                 i.savedKeyspan()
     746           1 :                 i.computeLargestPos()
     747           1 :         case posKeyspanEnd:
     748           1 :                 // Either a point key or the span's start key is previous.
     749           1 :                 if i.pointKey != nil && i.cmp(i.pointKey.UserKey, i.span.Start) >= 0 {
     750           1 :                         i.pos = posPointKey
     751           1 :                 } else {
     752           1 :                         i.pos = posKeyspanStart
     753           1 :                 }
     754           0 :         default:
     755           0 :                 panic(fmt.Sprintf("unexpected pos=%d", i.pos))
     756             :         }
     757             : }
     758             : 
     759             : func (i *InterleavingIter) yieldPosition(
     760             :         lowerBound []byte, advance func(),
     761           1 : ) (*base.InternalKey, base.LazyValue) {
     762           1 :         // This loop returns the first visible position in the current iteration
     763           1 :         // direction. Some positions are not visible and skipped. For example, if
     764           1 :         // masking is enabled and the iterator is positioned over a masked point
     765           1 :         // key, this loop skips the position. If a span's start key should be
     766           1 :         // interleaved next, but the span is empty, the loop continues to the next
     767           1 :         // key. Currently, span end keys are also always skipped, and are used only
     768           1 :         // for maintaining internal state.
     769           1 :         for {
     770           1 :                 switch i.pos {
     771           1 :                 case posExhausted:
     772           1 :                         return i.yieldNil()
     773           1 :                 case posPointKey:
     774           1 :                         if i.pointKey == nil {
     775           0 :                                 panic("i.pointKey is nil")
     776             :                         }
     777             : 
     778           1 :                         if i.mask != nil {
     779           1 :                                 i.maybeUpdateMask()
     780           1 :                                 if i.withinSpan && i.mask.SkipPoint(i.pointKey.UserKey) {
     781           1 :                                         // The span covers the point key. If a SkipPoint hook is
     782           1 :                                         // configured, ask it if we should skip this point key.
     783           1 :                                         if i.prefix != nil {
     784           1 :                                                 // During prefix-iteration node, once a point is masked,
     785           1 :                                                 // all subsequent keys with the same prefix must also be
     786           1 :                                                 // masked according to the key ordering. We can stop and
     787           1 :                                                 // return nil.
     788           1 :                                                 //
     789           1 :                                                 // NB: The above is not just an optimization. During
     790           1 :                                                 // prefix-iteration mode, the internal iterator contract
     791           1 :                                                 // prohibits us from Next-ing beyond the first key
     792           1 :                                                 // beyond the iteration prefix. If we didn't already
     793           1 :                                                 // stop early, we would need to check if this masked
     794           1 :                                                 // point is already beyond the prefix.
     795           1 :                                                 return i.yieldNil()
     796           1 :                                         }
     797             :                                         // TODO(jackson): If we thread a base.Comparer through to
     798             :                                         // InterleavingIter so that we have access to
     799             :                                         // ImmediateSuccessor, we could use NextPrefix. We'd need to
     800             :                                         // tweak the SpanMask interface slightly.
     801             : 
     802             :                                         // Advance beyond the masked point key.
     803           1 :                                         advance()
     804           1 :                                         continue
     805             :                                 }
     806             :                         }
     807           1 :                         return i.yieldPointKey()
     808           1 :                 case posKeyspanEnd:
     809           1 :                         // Don't interleave end keys; just advance.
     810           1 :                         advance()
     811           1 :                         continue
     812           1 :                 case posKeyspanStart:
     813           1 :                         // Don't interleave an empty span.
     814           1 :                         if i.span.Empty() {
     815           1 :                                 advance()
     816           1 :                                 continue
     817             :                         }
     818           1 :                         return i.yieldSyntheticSpanMarker(lowerBound)
     819           0 :                 default:
     820           0 :                         panic(fmt.Sprintf("unexpected interleavePos=%d", i.pos))
     821             :                 }
     822             :         }
     823             : }
     824             : 
     825             : // keyspanSeekGE seeks the keyspan iterator to the first span covering a key ≥ k.
     826           1 : func (i *InterleavingIter) keyspanSeekGE(k []byte, prefix []byte) {
     827           1 :         i.saveSpanForward(i.keyspanIter.SeekGE(k))
     828           1 :         i.savedKeyspan()
     829           1 : }
     830             : 
     831             : // keyspanSeekLT seeks the keyspan iterator to the last span covering a key < k.
     832           1 : func (i *InterleavingIter) keyspanSeekLT(k []byte) {
     833           1 :         i.saveSpanBackward(i.keyspanIter.SeekLT(k))
     834           1 :         // The current span's start key is not guaranteed to be less than key,
     835           1 :         // because of the bounds enforcement. Consider the following example:
     836           1 :         //
     837           1 :         // Bounds are set to [d,e). The user performs a SeekLT(d). The
     838           1 :         // FragmentIterator.SeekLT lands on a span [b,f). This span has a start key
     839           1 :         // less than d, as expected. Above, saveSpanBackward truncates the span to
     840           1 :         // match the iterator's current bounds, modifying the span to [d,e), which
     841           1 :         // does not overlap the search space of [-∞, d).
     842           1 :         //
     843           1 :         // This problem is a consequence of the SeekLT's exclusive search key and
     844           1 :         // the fact that we don't perform bounds truncation at every leaf iterator.
     845           1 :         if i.span != nil && i.truncated && i.cmp(i.truncatedSpan.Start, k) >= 0 {
     846           1 :                 i.span = nil
     847           1 :         }
     848           1 :         i.savedKeyspan()
     849             : }
     850             : 
     851           1 : func (i *InterleavingIter) saveSpanForward(span *Span, err error) {
     852           1 :         i.span = span
     853           1 :         i.err = firstError(i.err, err)
     854           1 :         i.truncated = false
     855           1 :         i.truncatedSpan = Span{}
     856           1 :         if i.span == nil {
     857           1 :                 return
     858           1 :         }
     859             :         // Check the upper bound if we have one.
     860           1 :         if i.upper != nil && i.cmp(i.span.Start, i.upper) >= 0 {
     861           1 :                 i.span = nil
     862           1 :                 return
     863           1 :         }
     864             : 
     865             :         // TODO(jackson): The key comparisons below truncate bounds whenever the
     866             :         // keyspan iterator is repositioned. We could perform this lazily, and do it
     867             :         // the first time the user actually asks for this span's bounds in
     868             :         // SpanBounds. This would reduce work in the case where there's no span
     869             :         // covering the point and the keyspan iterator is non-empty.
     870             : 
     871             :         // NB: These truncations don't require setting `keyspanMarkerTruncated`:
     872             :         // That flag only applies to truncated span marker keys.
     873           1 :         if i.lower != nil && i.cmp(i.span.Start, i.lower) < 0 {
     874           1 :                 i.truncated = true
     875           1 :                 i.truncatedSpan = *i.span
     876           1 :                 i.truncatedSpan.Start = i.lower
     877           1 :         }
     878           1 :         if i.upper != nil && i.cmp(i.upper, i.span.End) < 0 {
     879           1 :                 if !i.truncated {
     880           1 :                         i.truncated = true
     881           1 :                         i.truncatedSpan = *i.span
     882           1 :                 }
     883           1 :                 i.truncatedSpan.End = i.upper
     884             :         }
     885             :         // If this is a part of a SeekPrefixGE call, we may also need to truncate to
     886             :         // the prefix's bounds.
     887           1 :         if i.prefix != nil {
     888           1 :                 if !i.truncated {
     889           1 :                         i.truncated = true
     890           1 :                         i.truncatedSpan = *i.span
     891           1 :                 }
     892           1 :                 if i.cmp(i.prefix, i.truncatedSpan.Start) > 0 {
     893           1 :                         i.truncatedSpan.Start = i.prefix
     894           1 :                 }
     895           1 :                 i.nextPrefixBuf = i.comparer.ImmediateSuccessor(i.nextPrefixBuf[:0], i.prefix)
     896           1 :                 if i.truncated && i.cmp(i.nextPrefixBuf, i.truncatedSpan.End) < 0 {
     897           1 :                         i.truncatedSpan.End = i.nextPrefixBuf
     898           1 :                 }
     899             :         }
     900             : 
     901           1 :         if i.truncated && i.comparer.Equal(i.truncatedSpan.Start, i.truncatedSpan.End) {
     902           1 :                 i.span = nil
     903           1 :         }
     904             : }
     905             : 
     906           1 : func (i *InterleavingIter) saveSpanBackward(span *Span, err error) {
     907           1 :         i.span = span
     908           1 :         i.err = firstError(i.err, err)
     909           1 :         i.truncated = false
     910           1 :         i.truncatedSpan = Span{}
     911           1 :         if i.span == nil {
     912           1 :                 return
     913           1 :         }
     914             : 
     915             :         // Check the lower bound if we have one.
     916           1 :         if i.lower != nil && i.cmp(i.span.End, i.lower) <= 0 {
     917           0 :                 i.span = nil
     918           0 :                 return
     919           0 :         }
     920             : 
     921             :         // TODO(jackson): The key comparisons below truncate bounds whenever the
     922             :         // keyspan iterator is repositioned. We could perform this lazily, and do it
     923             :         // the first time the user actually asks for this span's bounds in
     924             :         // SpanBounds. This would reduce work in the case where there's no span
     925             :         // covering the point and the keyspan iterator is non-empty.
     926             : 
     927             :         // NB: These truncations don't require setting `keyspanMarkerTruncated`:
     928             :         // That flag only applies to truncated span marker keys.
     929           1 :         if i.lower != nil && i.cmp(i.span.Start, i.lower) < 0 {
     930           1 :                 i.truncated = true
     931           1 :                 i.truncatedSpan = *i.span
     932           1 :                 i.truncatedSpan.Start = i.lower
     933           1 :         }
     934           1 :         if i.upper != nil && i.cmp(i.upper, i.span.End) < 0 {
     935           1 :                 if !i.truncated {
     936           1 :                         i.truncated = true
     937           1 :                         i.truncatedSpan = *i.span
     938           1 :                 }
     939           1 :                 i.truncatedSpan.End = i.upper
     940             :         }
     941           1 :         if i.truncated && i.comparer.Equal(i.truncatedSpan.Start, i.truncatedSpan.End) {
     942           1 :                 i.span = nil
     943           1 :         }
     944             : }
     945             : 
     946           1 : func (i *InterleavingIter) yieldNil() (*base.InternalKey, base.LazyValue) {
     947           1 :         i.withinSpan = false
     948           1 :         i.clearMask()
     949           1 :         return i.verify(nil, base.LazyValue{})
     950           1 : }
     951             : 
     952           1 : func (i *InterleavingIter) yieldPointKey() (*base.InternalKey, base.LazyValue) {
     953           1 :         return i.verify(i.pointKey, i.pointVal)
     954           1 : }
     955             : 
     956             : func (i *InterleavingIter) yieldSyntheticSpanMarker(
     957             :         lowerBound []byte,
     958           1 : ) (*base.InternalKey, base.LazyValue) {
     959           1 :         i.spanMarker.UserKey = i.startKey()
     960           1 :         i.spanMarker.Trailer = base.MakeTrailer(base.InternalKeySeqNumMax, i.span.Keys[0].Kind())
     961           1 : 
     962           1 :         // Truncate the key we return to our lower bound if we have one. Note that
     963           1 :         // we use the lowerBound function parameter, not i.lower. The lowerBound
     964           1 :         // argument is guaranteed to be ≥ i.lower. It may be equal to the SetBounds
     965           1 :         // lower bound, or it could come from a SeekGE or SeekPrefixGE search key.
     966           1 :         if lowerBound != nil && i.cmp(lowerBound, i.startKey()) > 0 {
     967           1 :                 // Truncating to the lower bound may violate the upper bound if
     968           1 :                 // lowerBound == i.upper. For example, a SeekGE(k) uses k as a lower
     969           1 :                 // bound for truncating a span. The span a-z will be truncated to [k,
     970           1 :                 // z). If i.upper == k, we'd mistakenly try to return a span [k, k), an
     971           1 :                 // invariant violation.
     972           1 :                 if i.comparer.Equal(lowerBound, i.upper) {
     973           1 :                         return i.yieldNil()
     974           1 :                 }
     975             : 
     976             :                 // If the lowerBound argument came from a SeekGE or SeekPrefixGE
     977             :                 // call, and it may be backed by a user-provided byte slice that is not
     978             :                 // guaranteed to be stable.
     979             :                 //
     980             :                 // If the lowerBound argument is the lower bound set by SetBounds,
     981             :                 // Pebble owns the slice's memory. However, consider two successive
     982             :                 // calls to SetBounds(). The second may overwrite the lower bound.
     983             :                 // Although the external contract requires a seek after a SetBounds,
     984             :                 // Pebble's tests don't always. For this reason and to simplify
     985             :                 // reasoning around lifetimes, always copy the bound into keyBuf when
     986             :                 // truncating.
     987           1 :                 i.keyBuf = append(i.keyBuf[:0], lowerBound...)
     988           1 :                 i.spanMarker.UserKey = i.keyBuf
     989           1 :                 i.spanMarkerTruncated = true
     990             :         }
     991           1 :         i.maybeUpdateMask()
     992           1 :         return i.verify(&i.spanMarker, base.LazyValue{})
     993             : }
     994             : 
     995           1 : func (i *InterleavingIter) disablePrefixMode() {
     996           1 :         if i.prefix != nil {
     997           1 :                 i.prefix = nil
     998           1 :                 // Clear the existing span. It may not hold the true end bound of the
     999           1 :                 // underlying span.
    1000           1 :                 i.span = nil
    1001           1 :         }
    1002             : }
    1003             : 
    1004             : func (i *InterleavingIter) verify(
    1005             :         k *base.InternalKey, v base.LazyValue,
    1006           1 : ) (*base.InternalKey, base.LazyValue) {
    1007           1 :         // Wrap the entire function body in the invariants build tag, so that
    1008           1 :         // production builds elide this entire function.
    1009           1 :         if invariants.Enabled {
    1010           1 :                 switch {
    1011           0 :                 case i.dir == -1 && i.spanMarkerTruncated:
    1012           0 :                         panic("pebble: invariant violation: truncated span key in reverse iteration")
    1013           0 :                 case k != nil && i.lower != nil && i.cmp(k.UserKey, i.lower) < 0:
    1014           0 :                         panic("pebble: invariant violation: key < lower bound")
    1015           0 :                 case k != nil && i.upper != nil && i.cmp(k.UserKey, i.upper) >= 0:
    1016           0 :                         panic("pebble: invariant violation: key ≥ upper bound")
    1017           0 :                 case i.err != nil && k != nil:
    1018           0 :                         panic("pebble: invariant violation: accumulated error swallowed")
    1019           0 :                 case i.err == nil && i.pointIter.Error() != nil:
    1020           0 :                         panic("pebble: invariant violation: pointIter swallowed")
    1021             :                 }
    1022             :         }
    1023           1 :         return k, v
    1024             : }
    1025             : 
    1026           1 : func (i *InterleavingIter) savedKeyspan() {
    1027           1 :         i.spanMarkerTruncated = false
    1028           1 :         i.maskSpanChangedCalled = false
    1029           1 : }
    1030             : 
    1031             : // updateMask updates the current mask, if a mask is configured and the mask
    1032             : // hasn't been updated with the current keyspan yet.
    1033           1 : func (i *InterleavingIter) maybeUpdateMask() {
    1034           1 :         switch {
    1035           1 :         case i.mask == nil, i.maskSpanChangedCalled:
    1036           1 :                 return
    1037           1 :         case !i.withinSpan || i.span.Empty():
    1038           1 :                 i.clearMask()
    1039           1 :         case i.truncated:
    1040           1 :                 i.mask.SpanChanged(&i.truncatedSpan)
    1041           1 :                 i.maskSpanChangedCalled = true
    1042           1 :         default:
    1043           1 :                 i.mask.SpanChanged(i.span)
    1044           1 :                 i.maskSpanChangedCalled = true
    1045             :         }
    1046             : }
    1047             : 
    1048             : // clearMask clears the current mask, if a mask is configured and no mask should
    1049             : // be active.
    1050           1 : func (i *InterleavingIter) clearMask() {
    1051           1 :         if i.mask != nil {
    1052           1 :                 i.maskSpanChangedCalled = false
    1053           1 :                 i.mask.SpanChanged(nil)
    1054           1 :         }
    1055             : }
    1056             : 
    1057           1 : func (i *InterleavingIter) startKey() []byte {
    1058           1 :         if i.truncated {
    1059           1 :                 return i.truncatedSpan.Start
    1060           1 :         }
    1061           1 :         return i.span.Start
    1062             : }
    1063             : 
    1064           1 : func (i *InterleavingIter) savePoint(key *base.InternalKey, value base.LazyValue) {
    1065           1 :         i.pointKey, i.pointVal = key, value
    1066           1 :         if key == nil {
    1067           1 :                 i.err = firstError(i.err, i.pointIter.Error())
    1068           1 :         }
    1069           1 :         if invariants.Enabled {
    1070           1 :                 if err := i.pointIter.Error(); key != nil && err != nil {
    1071           0 :                         panic(errors.WithSecondaryError(
    1072           0 :                                 errors.AssertionFailedf("pebble: %T point iterator returned non-nil key %q while iter has error", i.pointIter, key),
    1073           0 :                                 err))
    1074             :                 }
    1075             :         }
    1076             : }
    1077             : 
    1078             : // Span returns the span covering the last key returned, if any. A span key is
    1079             : // considered to 'cover' a key if the key falls within the span's user key
    1080             : // bounds. The returned span is owned by the InterleavingIter. The caller is
    1081             : // responsible for copying if stability is required.
    1082             : //
    1083             : // Span will never return an invalid or empty span.
    1084           1 : func (i *InterleavingIter) Span() *Span {
    1085           1 :         if !i.withinSpan || len(i.span.Keys) == 0 {
    1086           1 :                 return nil
    1087           1 :         } else if i.truncated {
    1088           1 :                 return &i.truncatedSpan
    1089           1 :         }
    1090           1 :         return i.span
    1091             : }
    1092             : 
    1093             : // SetBounds implements (base.InternalIterator).SetBounds.
    1094           1 : func (i *InterleavingIter) SetBounds(lower, upper []byte) {
    1095           1 :         i.lower, i.upper = lower, upper
    1096           1 :         i.pointIter.SetBounds(lower, upper)
    1097           1 :         i.Invalidate()
    1098           1 : }
    1099             : 
    1100             : // SetContext implements (base.InternalIterator).SetContext.
    1101           0 : func (i *InterleavingIter) SetContext(ctx context.Context) {
    1102           0 :         i.pointIter.SetContext(ctx)
    1103           0 : }
    1104             : 
    1105             : // Invalidate invalidates the interleaving iterator's current position, clearing
    1106             : // its state. This prevents optimizations such as reusing the current span on
    1107             : // seek.
    1108           1 : func (i *InterleavingIter) Invalidate() {
    1109           1 :         i.span = nil
    1110           1 :         i.pointKey = nil
    1111           1 :         i.pointVal = base.LazyValue{}
    1112           1 : }
    1113             : 
    1114             : // Error implements (base.InternalIterator).Error.
    1115           1 : func (i *InterleavingIter) Error() error {
    1116           1 :         return i.err
    1117           1 : }
    1118             : 
    1119             : // Close implements (base.InternalIterator).Close.
    1120           1 : func (i *InterleavingIter) Close() error {
    1121           1 :         perr := i.pointIter.Close()
    1122           1 :         rerr := i.keyspanIter.Close()
    1123           1 :         return firstError(perr, rerr)
    1124           1 : }
    1125             : 
    1126             : // String implements (base.InternalIterator).String.
    1127           0 : func (i *InterleavingIter) String() string {
    1128           0 :         return fmt.Sprintf("keyspan-interleaving(%q)", i.pointIter.String())
    1129           0 : }
    1130             : 
    1131           1 : func firstError(err0, err1 error) error {
    1132           1 :         if err0 != nil {
    1133           1 :                 return err0
    1134           1 :         }
    1135           1 :         return err1
    1136             : }

Generated by: LCOV version 1.14