LCOV - code coverage report
Current view: top level - pebble/internal/keyspan - defragment.go (source / functions) Hit Total Coverage
Test: 2024-01-16 08:16Z 27d566eb - meta test only.lcov Lines: 264 290 91.0 %
Date: 2024-01-16 08:17:02 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2022 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             :         "bytes"
       9             : 
      10             :         "github.com/cockroachdb/pebble/internal/base"
      11             :         "github.com/cockroachdb/pebble/internal/bytealloc"
      12             :         "github.com/cockroachdb/pebble/internal/invariants"
      13             : )
      14             : 
      15             : // bufferReuseMaxCapacity is the maximum capacity of a DefragmentingIter buffer
      16             : // that DefragmentingIter will reuse. Buffers larger than this will be
      17             : // discarded and reallocated as necessary.
      18             : const bufferReuseMaxCapacity = 10 << 10 // 10 KB
      19             : 
      20             : // keysReuseMaxCapacity is the maximum capacity of a []keyspan.Key buffer that
      21             : // DefragmentingIter will reuse. Buffers larger than this will be discarded and
      22             : // reallocated as necessary.
      23             : const keysReuseMaxCapacity = 100
      24             : 
      25             : // DefragmentMethod configures the defragmentation performed by the
      26             : // DefragmentingIter.
      27             : type DefragmentMethod interface {
      28             :         // ShouldDefragment takes two abutting spans and returns whether the two
      29             :         // spans should be combined into a single, defragmented Span.
      30             :         ShouldDefragment(equal base.Equal, left, right *Span) bool
      31             : }
      32             : 
      33             : // The DefragmentMethodFunc type is an adapter to allow the use of ordinary
      34             : // functions as DefragmentMethods. If f is a function with the appropriate
      35             : // signature, DefragmentMethodFunc(f) is a DefragmentMethod that calls f.
      36             : type DefragmentMethodFunc func(equal base.Equal, left, right *Span) bool
      37             : 
      38             : // ShouldDefragment calls f(equal, left, right).
      39           1 : func (f DefragmentMethodFunc) ShouldDefragment(equal base.Equal, left, right *Span) bool {
      40           1 :         return f(equal, left, right)
      41           1 : }
      42             : 
      43             : // DefragmentInternal configures a DefragmentingIter to defragment spans
      44             : // only if they have identical keys. It requires spans' keys to be sorted in
      45             : // trailer descending order.
      46             : //
      47             : // This defragmenting method is intended for use in compactions that may see
      48             : // internal range keys fragments that may now be joined, because the state that
      49             : // required their fragmentation has been dropped.
      50           1 : var DefragmentInternal DefragmentMethod = DefragmentMethodFunc(func(equal base.Equal, a, b *Span) bool {
      51           1 :         if a.KeysOrder != ByTrailerDesc || b.KeysOrder != ByTrailerDesc {
      52           0 :                 panic("pebble: span keys unexpectedly not in trailer descending order")
      53             :         }
      54           1 :         if len(a.Keys) != len(b.Keys) {
      55           1 :                 return false
      56           1 :         }
      57           1 :         for i := range a.Keys {
      58           1 :                 if a.Keys[i].Trailer != b.Keys[i].Trailer {
      59           1 :                         return false
      60           1 :                 }
      61           1 :                 if !equal(a.Keys[i].Suffix, b.Keys[i].Suffix) {
      62           1 :                         return false
      63           1 :                 }
      64           1 :                 if !bytes.Equal(a.Keys[i].Value, b.Keys[i].Value) {
      65           1 :                         return false
      66           1 :                 }
      67             :         }
      68           1 :         return true
      69             : })
      70             : 
      71             : // DefragmentReducer merges the current and next Key slices, returning a new Key
      72             : // slice.
      73             : //
      74             : // Implementations should modify and return `cur` to save on allocations, or
      75             : // consider allocating a new slice, as the `cur` slice may be retained by the
      76             : // DefragmentingIter and mutated. The `next` slice must not be mutated.
      77             : //
      78             : // The incoming slices are sorted by (SeqNum, Kind) descending. The output slice
      79             : // must also have this sort order.
      80             : type DefragmentReducer func(cur, next []Key) []Key
      81             : 
      82             : // StaticDefragmentReducer is a no-op DefragmentReducer that simply returns the
      83             : // current key slice, effectively retaining the first set of keys encountered
      84             : // for a defragmented span.
      85             : //
      86             : // This reducer can be used, for example, when the set of Keys for each Span
      87             : // being reduced is not expected to change, and therefore the keys from the
      88             : // first span encountered can be used without considering keys in subsequent
      89             : // spans.
      90           1 : var StaticDefragmentReducer DefragmentReducer = func(cur, _ []Key) []Key {
      91           1 :         return cur
      92           1 : }
      93             : 
      94             : // iterPos is an enum indicating the position of the defragmenting iter's
      95             : // wrapped iter. The defragmenting iter must look ahead or behind when
      96             : // defragmenting forward or backwards respectively, and this enum records that
      97             : // current position.
      98             : type iterPos int8
      99             : 
     100             : const (
     101             :         iterPosPrev iterPos = -1
     102             :         iterPosCurr iterPos = 0
     103             :         iterPosNext iterPos = +1
     104             : )
     105             : 
     106             : // DefragmentingIter wraps a key span iterator, defragmenting physical
     107             : // fragmentation during iteration.
     108             : //
     109             : // During flushes and compactions, keys applied over a span may be split at
     110             : // sstable boundaries. This fragmentation can produce internal key bounds that
     111             : // do not match any of the bounds ever supplied to a user operation. This
     112             : // physical fragmentation is necessary to avoid excessively wide sstables.
     113             : //
     114             : // The defragmenting iterator undoes this physical fragmentation, joining spans
     115             : // with abutting bounds and equal state. The defragmenting iterator takes a
     116             : // DefragmentMethod to determine what is "equal state" for a span. The
     117             : // DefragmentMethod is a function type, allowing arbitrary comparisons between
     118             : // Span keys.
     119             : //
     120             : // Seeking (SeekGE, SeekLT) poses an obstacle to defragmentation. A seek may
     121             : // land on a physical fragment in the middle of several fragments that must be
     122             : // defragmented. A seek that lands in a fragment straddling the seek key must
     123             : // first degfragment in the opposite direction of iteration to find the
     124             : // beginning of the defragmented span, and then defragments in the iteration
     125             : // direction, ensuring it's found a whole defragmented span.
     126             : type DefragmentingIter struct {
     127             :         // DefragmentingBuffers holds buffers used for copying iterator state.
     128             :         *DefragmentingBuffers
     129             :         comparer *base.Comparer
     130             :         equal    base.Equal
     131             :         iter     FragmentIterator
     132             :         iterSpan *Span
     133             :         iterPos  iterPos
     134             : 
     135             :         // curr holds the span at the current iterator position.
     136             :         curr Span
     137             : 
     138             :         // method is a comparison function for two spans. method is called when two
     139             :         // spans are abutting to determine whether they may be defragmented.
     140             :         // method does not itself check for adjacency for the two spans.
     141             :         method DefragmentMethod
     142             : 
     143             :         // reduce is the reducer function used to collect Keys across all spans that
     144             :         // constitute a defragmented span.
     145             :         reduce DefragmentReducer
     146             : }
     147             : 
     148             : // DefragmentingBuffers holds buffers used for copying iterator state.
     149             : type DefragmentingBuffers struct {
     150             :         // currBuf is a buffer for use when copying user keys for curr. currBuf is
     151             :         // cleared between positioning methods.
     152             :         currBuf bytealloc.A
     153             :         // keysBuf is a buffer for use when copying Keys for DefragmentingIter.curr.
     154             :         keysBuf []Key
     155             :         // keyBuf is a buffer specifically for the defragmented start key when
     156             :         // defragmenting backwards or the defragmented end key when defragmenting
     157             :         // forwards. These bounds are overwritten repeatedly during defragmentation,
     158             :         // and the defragmentation routines overwrite keyBuf repeatedly to store
     159             :         // these extended bounds.
     160             :         keyBuf []byte
     161             : }
     162             : 
     163             : // PrepareForReuse discards any excessively large buffers.
     164           1 : func (bufs *DefragmentingBuffers) PrepareForReuse() {
     165           1 :         if cap(bufs.currBuf) > bufferReuseMaxCapacity {
     166           1 :                 bufs.currBuf = nil
     167           1 :         }
     168           1 :         if cap(bufs.keyBuf) > bufferReuseMaxCapacity {
     169           0 :                 bufs.keyBuf = nil
     170           0 :         }
     171           1 :         if cap(bufs.keysBuf) > keysReuseMaxCapacity {
     172           0 :                 bufs.keysBuf = nil
     173           0 :         }
     174             : }
     175             : 
     176             : // Assert that *DefragmentingIter implements the FragmentIterator interface.
     177             : var _ FragmentIterator = (*DefragmentingIter)(nil)
     178             : 
     179             : // Init initializes the defragmenting iter using the provided defragment
     180             : // method.
     181             : func (i *DefragmentingIter) Init(
     182             :         comparer *base.Comparer,
     183             :         iter FragmentIterator,
     184             :         equal DefragmentMethod,
     185             :         reducer DefragmentReducer,
     186             :         bufs *DefragmentingBuffers,
     187           1 : ) {
     188           1 :         *i = DefragmentingIter{
     189           1 :                 DefragmentingBuffers: bufs,
     190           1 :                 comparer:             comparer,
     191           1 :                 equal:                comparer.Equal,
     192           1 :                 iter:                 iter,
     193           1 :                 method:               equal,
     194           1 :                 reduce:               reducer,
     195           1 :         }
     196           1 : }
     197             : 
     198             : // Error returns any accumulated error.
     199           1 : func (i *DefragmentingIter) Error() error {
     200           1 :         return i.iter.Error()
     201           1 : }
     202             : 
     203             : // Close closes the underlying iterators.
     204           1 : func (i *DefragmentingIter) Close() error {
     205           1 :         return i.iter.Close()
     206           1 : }
     207             : 
     208             : // SeekGE moves the iterator to the first span covering a key greater than or
     209             : // equal to the given key. This is equivalent to seeking to the first span with
     210             : // an end key greater than the given key.
     211           1 : func (i *DefragmentingIter) SeekGE(key []byte) *Span {
     212           1 :         i.iterSpan = i.iter.SeekGE(key)
     213           1 :         if i.iterSpan == nil {
     214           1 :                 i.iterPos = iterPosCurr
     215           1 :                 return nil
     216           1 :         } else if i.iterSpan.Empty() {
     217           1 :                 i.iterPos = iterPosCurr
     218           1 :                 return i.iterSpan
     219           1 :         }
     220             :         // If the span starts strictly after key, we know there mustn't be an
     221             :         // earlier span that ends at i.iterSpan.Start, otherwise i.iter would've
     222             :         // returned that span instead.
     223           1 :         if i.comparer.Compare(i.iterSpan.Start, key) > 0 {
     224           1 :                 return i.defragmentForward()
     225           1 :         }
     226             : 
     227             :         // The span we landed on has a Start bound ≤ key. There may be additional
     228             :         // fragments before this span. Defragment backward to find the start of the
     229             :         // defragmented span.
     230           1 :         i.defragmentBackward()
     231           1 : 
     232           1 :         // Defragmenting backward may have stopped because it encountered an error.
     233           1 :         // If so, we must not continue so that i.iter.Error() (and thus i.Error())
     234           1 :         // yields the error.
     235           1 :         if i.iterSpan == nil && i.iter.Error() != nil {
     236           0 :                 return nil
     237           0 :         }
     238             : 
     239           1 :         if i.iterPos == iterPosPrev {
     240           1 :                 // Next once back onto the span.
     241           1 :                 i.iterSpan = i.iter.Next()
     242           1 :         }
     243             :         // Defragment the full span from its start.
     244           1 :         return i.defragmentForward()
     245             : }
     246             : 
     247             : // SeekLT moves the iterator to the last span covering a key less than the
     248             : // given key. This is equivalent to seeking to the last span with a start
     249             : // key less than the given key.
     250           1 : func (i *DefragmentingIter) SeekLT(key []byte) *Span {
     251           1 :         i.iterSpan = i.iter.SeekLT(key)
     252           1 :         if i.iterSpan == nil {
     253           1 :                 i.iterPos = iterPosCurr
     254           1 :                 return nil
     255           1 :         } else if i.iterSpan.Empty() {
     256           1 :                 i.iterPos = iterPosCurr
     257           1 :                 return i.iterSpan
     258           1 :         }
     259             :         // If the span ends strictly before key, we know there mustn't be a later
     260             :         // span that starts at i.iterSpan.End, otherwise i.iter would've returned
     261             :         // that span instead.
     262           1 :         if i.comparer.Compare(i.iterSpan.End, key) < 0 {
     263           1 :                 return i.defragmentBackward()
     264           1 :         }
     265             : 
     266             :         // The span we landed on has a End bound ≥ key. There may be additional
     267             :         // fragments after this span. Defragment forward to find the end of the
     268             :         // defragmented span.
     269           1 :         i.defragmentForward()
     270           1 : 
     271           1 :         // Defragmenting forward may have stopped because it encountered an error.
     272           1 :         // If so, we must not continue so that i.iter.Error() (and thus i.Error())
     273           1 :         // yields the error.
     274           1 :         if i.iterSpan == nil && i.iter.Error() != nil {
     275           0 :                 return nil
     276           0 :         }
     277             : 
     278           1 :         if i.iterPos == iterPosNext {
     279           1 :                 // Prev once back onto the span.
     280           1 :                 i.iterSpan = i.iter.Prev()
     281           1 :         }
     282             :         // Defragment the full span from its end.
     283           1 :         return i.defragmentBackward()
     284             : }
     285             : 
     286             : // First seeks the iterator to the first span and returns it.
     287           1 : func (i *DefragmentingIter) First() *Span {
     288           1 :         i.iterSpan = i.iter.First()
     289           1 :         if i.iterSpan == nil {
     290           1 :                 i.iterPos = iterPosCurr
     291           1 :                 return nil
     292           1 :         }
     293           1 :         return i.defragmentForward()
     294             : }
     295             : 
     296             : // Last seeks the iterator to the last span and returns it.
     297           1 : func (i *DefragmentingIter) Last() *Span {
     298           1 :         i.iterSpan = i.iter.Last()
     299           1 :         if i.iterSpan == nil {
     300           1 :                 i.iterPos = iterPosCurr
     301           1 :                 return nil
     302           1 :         }
     303           1 :         return i.defragmentBackward()
     304             : }
     305             : 
     306             : // Next advances to the next span and returns it.
     307           1 : func (i *DefragmentingIter) Next() *Span {
     308           1 :         switch i.iterPos {
     309           1 :         case iterPosPrev:
     310           1 :                 // Switching directions; The iterator is currently positioned over the
     311           1 :                 // last span of the previous set of fragments. In the below diagram,
     312           1 :                 // the iterator is positioned over the last span that contributes to
     313           1 :                 // the defragmented x position. We want to be positioned over the first
     314           1 :                 // span that contributes to the z position.
     315           1 :                 //
     316           1 :                 //   x x x y y y z z z
     317           1 :                 //       ^       ^
     318           1 :                 //      old     new
     319           1 :                 //
     320           1 :                 // Next once to move onto y, defragment forward to land on the first z
     321           1 :                 // position.
     322           1 :                 i.iterSpan = i.iter.Next()
     323           1 :                 if invariants.Enabled && i.iterSpan == nil && i.iter.Error() == nil {
     324           0 :                         panic("pebble: invariant violation: no next span while switching directions")
     325             :                 }
     326             :                 // We're now positioned on the first span that was defragmented into the
     327             :                 // current iterator position. Skip over the rest of the current iterator
     328             :                 // position's constitutent fragments. In the above example, this would
     329             :                 // land on the first 'z'.
     330           1 :                 i.defragmentForward()
     331           1 :                 if i.iterSpan == nil {
     332           1 :                         i.iterPos = iterPosCurr
     333           1 :                         return nil
     334           1 :                 }
     335             : 
     336             :                 // Now that we're positioned over the first of the next set of
     337             :                 // fragments, defragment forward.
     338           1 :                 return i.defragmentForward()
     339           1 :         case iterPosCurr:
     340           1 :                 // iterPosCurr is only used when the iter is exhausted or when the iterator
     341           1 :                 // is at an empty span.
     342           1 :                 if invariants.Enabled && i.iterSpan != nil && !i.iterSpan.Empty() {
     343           0 :                         panic("pebble: invariant violation: iterPosCurr with valid iterSpan")
     344             :                 }
     345             : 
     346           1 :                 i.iterSpan = i.iter.Next()
     347           1 :                 if i.iterSpan == nil {
     348           1 :                         return nil
     349           1 :                 }
     350           1 :                 return i.defragmentForward()
     351           1 :         case iterPosNext:
     352           1 :                 // Already at the next span.
     353           1 :                 if i.iterSpan == nil {
     354           1 :                         i.iterPos = iterPosCurr
     355           1 :                         return nil
     356           1 :                 }
     357           1 :                 return i.defragmentForward()
     358           0 :         default:
     359           0 :                 panic("unreachable")
     360             :         }
     361             : }
     362             : 
     363             : // Prev steps back to the previous span and returns it.
     364           1 : func (i *DefragmentingIter) Prev() *Span {
     365           1 :         switch i.iterPos {
     366           1 :         case iterPosPrev:
     367           1 :                 // Already at the previous span.
     368           1 :                 if i.iterSpan == nil {
     369           1 :                         i.iterPos = iterPosCurr
     370           1 :                         return nil
     371           1 :                 }
     372           1 :                 return i.defragmentBackward()
     373           1 :         case iterPosCurr:
     374           1 :                 // iterPosCurr is only used when the iter is exhausted or when the iterator
     375           1 :                 // is at an empty span.
     376           1 :                 if invariants.Enabled && i.iterSpan != nil && !i.iterSpan.Empty() {
     377           0 :                         panic("pebble: invariant violation: iterPosCurr with valid iterSpan")
     378             :                 }
     379             : 
     380           1 :                 i.iterSpan = i.iter.Prev()
     381           1 :                 if i.iterSpan == nil {
     382           1 :                         return nil
     383           1 :                 }
     384           1 :                 return i.defragmentBackward()
     385           1 :         case iterPosNext:
     386           1 :                 // Switching directions; The iterator is currently positioned over the
     387           1 :                 // first fragment of the next set of fragments. In the below diagram,
     388           1 :                 // the iterator is positioned over the first span that contributes to
     389           1 :                 // the defragmented z position. We want to be positioned over the last
     390           1 :                 // span that contributes to the x position.
     391           1 :                 //
     392           1 :                 //   x x x y y y z z z
     393           1 :                 //       ^       ^
     394           1 :                 //      new     old
     395           1 :                 //
     396           1 :                 // Prev once to move onto y, defragment backward to land on the last x
     397           1 :                 // position.
     398           1 :                 i.iterSpan = i.iter.Prev()
     399           1 :                 if invariants.Enabled && i.iterSpan == nil && i.iter.Error() == nil {
     400           0 :                         panic("pebble: invariant violation: no previous span while switching directions")
     401             :                 }
     402             :                 // We're now positioned on the last span that was defragmented into the
     403             :                 // current iterator position. Skip over the rest of the current iterator
     404             :                 // position's constitutent fragments. In the above example, this would
     405             :                 // land on the last 'x'.
     406           1 :                 i.defragmentBackward()
     407           1 : 
     408           1 :                 // Now that we're positioned over the last of the prev set of
     409           1 :                 // fragments, defragment backward.
     410           1 :                 if i.iterSpan == nil {
     411           1 :                         i.iterPos = iterPosCurr
     412           1 :                         return nil
     413           1 :                 }
     414           1 :                 return i.defragmentBackward()
     415           0 :         default:
     416           0 :                 panic("unreachable")
     417             :         }
     418             : }
     419             : 
     420             : // checkEqual checks the two spans for logical equivalence. It uses the passed-in
     421             : // DefragmentMethod and ensures both spans are NOT empty; not defragmenting empty
     422             : // spans is an optimization that lets us load fewer sstable blocks.
     423           1 : func (i *DefragmentingIter) checkEqual(left, right *Span) bool {
     424           1 :         return (!left.Empty() && !right.Empty()) && i.method.ShouldDefragment(i.equal, i.iterSpan, &i.curr)
     425           1 : }
     426             : 
     427             : // defragmentForward defragments spans in the forward direction, starting from
     428             : // i.iter's current position. The span at the current position must be non-nil,
     429             : // but may be Empty().
     430           1 : func (i *DefragmentingIter) defragmentForward() *Span {
     431           1 :         if i.iterSpan.Empty() {
     432           1 :                 // An empty span will never be equal to another span; see checkEqual for
     433           1 :                 // why. To avoid loading non-empty range keys further ahead by calling Next,
     434           1 :                 // return early.
     435           1 :                 i.iterPos = iterPosCurr
     436           1 :                 return i.iterSpan
     437           1 :         }
     438           1 :         i.saveCurrent()
     439           1 : 
     440           1 :         i.iterPos = iterPosNext
     441           1 :         i.iterSpan = i.iter.Next()
     442           1 :         for i.iterSpan != nil {
     443           1 :                 if !i.equal(i.curr.End, i.iterSpan.Start) {
     444           1 :                         // Not a continuation.
     445           1 :                         break
     446             :                 }
     447           1 :                 if !i.checkEqual(i.iterSpan, &i.curr) {
     448           1 :                         // Not a continuation.
     449           1 :                         break
     450             :                 }
     451           1 :                 i.keyBuf = append(i.keyBuf[:0], i.iterSpan.End...)
     452           1 :                 i.curr.End = i.keyBuf
     453           1 :                 i.keysBuf = i.reduce(i.keysBuf, i.iterSpan.Keys)
     454           1 :                 i.iterSpan = i.iter.Next()
     455             :         }
     456             :         // i.iterSpan == nil
     457             :         //
     458             :         // The inner iterator may return nil when it encounters an error. If there
     459             :         // was an error, we don't know whether there is another span we should
     460             :         // defragment or not. Return nil so that the caller knows they should check
     461             :         // Error().
     462           1 :         if i.iter.Error() != nil {
     463           0 :                 return nil
     464           0 :         }
     465           1 :         i.curr.Keys = i.keysBuf
     466           1 :         return &i.curr
     467             : }
     468             : 
     469             : // defragmentBackward defragments spans in the backward direction, starting from
     470             : // i.iter's current position. The span at the current position must be non-nil,
     471             : // but may be Empty().
     472           1 : func (i *DefragmentingIter) defragmentBackward() *Span {
     473           1 :         if i.iterSpan.Empty() {
     474           1 :                 // An empty span will never be equal to another span; see checkEqual for
     475           1 :                 // why. To avoid loading non-empty range keys further ahead by calling Next,
     476           1 :                 // return early.
     477           1 :                 i.iterPos = iterPosCurr
     478           1 :                 return i.iterSpan
     479           1 :         }
     480           1 :         i.saveCurrent()
     481           1 : 
     482           1 :         i.iterPos = iterPosPrev
     483           1 :         i.iterSpan = i.iter.Prev()
     484           1 :         for i.iterSpan != nil {
     485           1 :                 if !i.equal(i.curr.Start, i.iterSpan.End) {
     486           1 :                         // Not a continuation.
     487           1 :                         break
     488             :                 }
     489           1 :                 if !i.checkEqual(i.iterSpan, &i.curr) {
     490           1 :                         // Not a continuation.
     491           1 :                         break
     492             :                 }
     493           1 :                 i.keyBuf = append(i.keyBuf[:0], i.iterSpan.Start...)
     494           1 :                 i.curr.Start = i.keyBuf
     495           1 :                 i.keysBuf = i.reduce(i.keysBuf, i.iterSpan.Keys)
     496           1 :                 i.iterSpan = i.iter.Prev()
     497             :         }
     498             :         // i.iterSpan == nil
     499             :         //
     500             :         // The inner iterator may return nil when it encounters an error. If there
     501             :         // was an error, we don't know whether there is another span we should
     502             :         // defragment or not. Return nil so that the caller knows they should check
     503             :         // Error().
     504           1 :         if i.iter.Error() != nil {
     505           0 :                 return nil
     506           0 :         }
     507           1 :         i.curr.Keys = i.keysBuf
     508           1 :         return &i.curr
     509             : }
     510             : 
     511           1 : func (i *DefragmentingIter) saveCurrent() {
     512           1 :         i.currBuf.Reset()
     513           1 :         i.keysBuf = i.keysBuf[:0]
     514           1 :         i.keyBuf = i.keyBuf[:0]
     515           1 :         if i.iterSpan == nil {
     516           0 :                 return
     517           0 :         }
     518           1 :         i.curr = Span{
     519           1 :                 Start:     i.saveBytes(i.iterSpan.Start),
     520           1 :                 End:       i.saveBytes(i.iterSpan.End),
     521           1 :                 KeysOrder: i.iterSpan.KeysOrder,
     522           1 :         }
     523           1 :         for j := range i.iterSpan.Keys {
     524           1 :                 i.keysBuf = append(i.keysBuf, Key{
     525           1 :                         Trailer: i.iterSpan.Keys[j].Trailer,
     526           1 :                         Suffix:  i.saveBytes(i.iterSpan.Keys[j].Suffix),
     527           1 :                         Value:   i.saveBytes(i.iterSpan.Keys[j].Value),
     528           1 :                 })
     529           1 :         }
     530           1 :         i.curr.Keys = i.keysBuf
     531             : }
     532             : 
     533           1 : func (i *DefragmentingIter) saveBytes(b []byte) []byte {
     534           1 :         if b == nil {
     535           1 :                 return nil
     536           1 :         }
     537           1 :         i.currBuf, b = i.currBuf.Copy(b)
     538           1 :         return b
     539             : }
     540             : 
     541             : // WrapChildren implements FragmentIterator.
     542           0 : func (i *DefragmentingIter) WrapChildren(wrap WrapFn) {
     543           0 :         i.iter = wrap(i.iter)
     544           0 : }

Generated by: LCOV version 1.14