LCOV - code coverage report
Current view: top level - pebble/internal/keyspan - truncate.go (source / functions) Hit Total Coverage
Test: 2024-06-16 08:16Z 0e4fb77b - meta test only.lcov Lines: 81 104 77.9 %
Date: 2024-06-16 08:16:53 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2019 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             :         "github.com/cockroachdb/pebble/internal/base"
       9             :         "github.com/cockroachdb/pebble/internal/invariants"
      10             : )
      11             : 
      12             : // Truncate creates a new iterator where every span in the supplied iterator is
      13             : // truncated to be contained within the given user key bounds.
      14             : //
      15             : // Note that fragment iterator Spans always have exclusive end-keys; if the
      16             : // given bounds have an inclusive end key, then the input iterator must not
      17             : // produce a span that contains that key. The only difference between bounds.End
      18             : // being inclusive vs exclusive is this extra check.
      19           1 : func Truncate(cmp base.Compare, iter FragmentIterator, bounds base.UserKeyBounds) FragmentIterator {
      20           1 :         return &truncatingIter{
      21           1 :                 iter:   iter,
      22           1 :                 cmp:    cmp,
      23           1 :                 bounds: bounds,
      24           1 :         }
      25           1 : }
      26             : 
      27             : type truncatingIter struct {
      28             :         iter FragmentIterator
      29             :         cmp  base.Compare
      30             : 
      31             :         bounds base.UserKeyBounds
      32             : 
      33             :         span Span
      34             : }
      35             : 
      36             : // SeekGE implements FragmentIterator.
      37           1 : func (i *truncatingIter) SeekGE(key []byte) (*Span, error) {
      38           1 :         span, err := i.iter.SeekGE(key)
      39           1 :         if err != nil {
      40           0 :                 return nil, err
      41           0 :         }
      42           1 :         span, spanBoundsChanged, err := i.nextSpanWithinBounds(span, +1)
      43           1 :         if err != nil {
      44           0 :                 return nil, err
      45           0 :         }
      46             :         // nextSpanWithinBounds could return a span that's less than key, if the end
      47             :         // bound was truncated to end at a key less than or equal to `key`. Detect
      48             :         // this case and next/invalidate the iter.
      49           1 :         if spanBoundsChanged && i.cmp(span.End, key) <= 0 {
      50           1 :                 return i.Next()
      51           1 :         }
      52           1 :         return span, nil
      53             : }
      54             : 
      55             : // SeekLT implements FragmentIterator.
      56           1 : func (i *truncatingIter) SeekLT(key []byte) (*Span, error) {
      57           1 :         span, err := i.iter.SeekLT(key)
      58           1 :         if err != nil {
      59           0 :                 return nil, err
      60           0 :         }
      61           1 :         span, spanBoundsChanged, err := i.nextSpanWithinBounds(span, -1)
      62           1 :         if err != nil {
      63           0 :                 return nil, err
      64           0 :         }
      65             :         // nextSpanWithinBounds could return a span that's >= key, if the start bound
      66             :         // was truncated to start at a key greater than or equal to `key`. Detect this
      67             :         // case and prev/invalidate the iter.
      68           1 :         if spanBoundsChanged && i.cmp(span.Start, key) >= 0 {
      69           1 :                 return i.Prev()
      70           1 :         }
      71           1 :         return span, nil
      72             : }
      73             : 
      74             : // First implements FragmentIterator.
      75           1 : func (i *truncatingIter) First() (*Span, error) {
      76           1 :         span, err := i.iter.First()
      77           1 :         if err != nil {
      78           0 :                 return nil, err
      79           0 :         }
      80           1 :         span, _, err = i.nextSpanWithinBounds(span, +1)
      81           1 :         return span, err
      82             : }
      83             : 
      84             : // Last implements FragmentIterator.
      85           1 : func (i *truncatingIter) Last() (*Span, error) {
      86           1 :         span, err := i.iter.Last()
      87           1 :         if err != nil {
      88           0 :                 return nil, err
      89           0 :         }
      90           1 :         span, _, err = i.nextSpanWithinBounds(span, -1)
      91           1 :         return span, err
      92             : }
      93             : 
      94             : // Next implements FragmentIterator.
      95           1 : func (i *truncatingIter) Next() (*Span, error) {
      96           1 :         span, err := i.iter.Next()
      97           1 :         if err != nil {
      98           0 :                 return nil, err
      99           0 :         }
     100           1 :         span, _, err = i.nextSpanWithinBounds(span, +1)
     101           1 :         return span, err
     102             : }
     103             : 
     104             : // Prev implements FragmentIterator.
     105           1 : func (i *truncatingIter) Prev() (*Span, error) {
     106           1 :         span, err := i.iter.Prev()
     107           1 :         if err != nil {
     108           0 :                 return nil, err
     109           0 :         }
     110           1 :         span, _, err = i.nextSpanWithinBounds(span, -1)
     111           1 :         return span, err
     112             : }
     113             : 
     114             : // Close implements FragmentIterator.
     115           1 : func (i *truncatingIter) Close() error {
     116           1 :         return i.iter.Close()
     117           1 : }
     118             : 
     119             : // nextSpanWithinBounds returns the first span (starting with the given span and
     120             : // advancing in the given direction) that intersects the bounds. It returns a
     121             : // span that is entirely within the bounds; spanBoundsChanged indicates if the span
     122             : // bounds had to be truncated.
     123             : func (i *truncatingIter) nextSpanWithinBounds(
     124             :         span *Span, dir int8,
     125           1 : ) (_ *Span, spanBoundsChanged bool, _ error) {
     126           1 :         var err error
     127           1 :         for span != nil {
     128           1 :                 if i.bounds.End.Kind == base.Inclusive && span.Contains(i.cmp, i.bounds.End.Key) {
     129           0 :                         err := base.AssertionFailedf("inclusive upper bound %q inside span %s", i.bounds.End.Key, span)
     130           0 :                         if invariants.Enabled {
     131           0 :                                 panic(err)
     132             :                         }
     133           0 :                         return nil, false, err
     134             :                 }
     135             :                 // Intersect [span.Start, span.End) with [i.bounds.Start, i.bounds.End.Key).
     136           1 :                 spanBoundsChanged = false
     137           1 :                 start := span.Start
     138           1 :                 if i.cmp(start, i.bounds.Start) < 0 {
     139           1 :                         spanBoundsChanged = true
     140           1 :                         start = i.bounds.Start
     141           1 :                 }
     142           1 :                 end := span.End
     143           1 :                 if i.cmp(end, i.bounds.End.Key) > 0 {
     144           1 :                         spanBoundsChanged = true
     145           1 :                         end = i.bounds.End.Key
     146           1 :                 }
     147           1 :                 if !spanBoundsChanged {
     148           1 :                         return span, false, nil
     149           1 :                 }
     150           1 :                 if i.cmp(start, end) < 0 {
     151           1 :                         i.span = Span{
     152           1 :                                 Start:     start,
     153           1 :                                 End:       end,
     154           1 :                                 Keys:      span.Keys,
     155           1 :                                 KeysOrder: span.KeysOrder,
     156           1 :                         }
     157           1 :                         return &i.span, true, nil
     158           1 :                 }
     159             :                 // Span is outside of bounds, find the next one.
     160           1 :                 if dir == +1 {
     161           1 :                         span, err = i.iter.Next()
     162           1 :                 } else {
     163           1 :                         span, err = i.iter.Prev()
     164           1 :                 }
     165             :         }
     166             :         // NB: err may be nil or non-nil.
     167           1 :         return nil, false, err
     168             : }
     169             : 
     170             : // WrapChildren implements FragmentIterator.
     171           0 : func (i *truncatingIter) WrapChildren(wrap WrapFn) {
     172           0 :         i.iter = wrap(i.iter)
     173           0 : }

Generated by: LCOV version 1.14