github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/internal/keyspan/filter.go (about) 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 "github.com/cockroachdb/pebble/internal/base" 8 9 // FilterFunc defines a transform from the input Span into the output Span. The 10 // function returns true if the Span should be returned by the iterator, and 11 // false if the Span should be skipped. The FilterFunc is permitted to mutate 12 // the output Span, for example, to elice certain keys, or update the Span's 13 // bounds if so desired. The output Span's Keys slice may be reused to reduce 14 // allocations. 15 type FilterFunc func(in *Span, out *Span) (keep bool) 16 17 // filteringIter is a FragmentIterator that uses a FilterFunc to select which 18 // Spans from the input iterator are returned in the output. 19 // 20 // A note on Span lifetimes: as the FilterFunc reuses a Span with a mutable 21 // slice of Keys to reduce allocations, Spans returned by this iterator are only 22 // valid until the next relative or absolute positioning method is called. 23 type filteringIter struct { 24 iter FragmentIterator 25 filterFn FilterFunc 26 cmp base.Compare 27 28 // span is a mutable Span passed to the filterFn. The filterFn is free to 29 // mutate this Span. The slice of Keys in the Span is reused with every call 30 // to the filterFn. 31 span Span 32 } 33 34 var _ FragmentIterator = (*filteringIter)(nil) 35 36 // Filter returns a new filteringIter that will filter the Spans from the 37 // provided child iterator using the provided FilterFunc. 38 func Filter(iter FragmentIterator, filter FilterFunc, cmp base.Compare) FragmentIterator { 39 return &filteringIter{iter: iter, filterFn: filter, cmp: cmp} 40 } 41 42 // SeekGE implements FragmentIterator. 43 func (i *filteringIter) SeekGE(key []byte) *Span { 44 span := i.filter(i.iter.SeekGE(key), +1) 45 // i.filter could return a span that's less than key, _if_ the filterFunc 46 // (which has no knowledge of the seek key) mutated the span to end at a key 47 // less than or equal to `key`. Detect this case and next/invalidate the iter. 48 if span != nil && i.cmp(span.End, key) <= 0 { 49 return i.Next() 50 } 51 return span 52 } 53 54 // SeekLT implements FragmentIterator. 55 func (i *filteringIter) SeekLT(key []byte) *Span { 56 span := i.filter(i.iter.SeekLT(key), -1) 57 // i.filter could return a span that's >= key, _if_ the filterFunc (which has 58 // no knowledge of the seek key) mutated the span to start at a key greater 59 // than or equal to `key`. Detect this case and prev/invalidate the iter. 60 if span != nil && i.cmp(span.Start, key) >= 0 { 61 return i.Prev() 62 } 63 return span 64 } 65 66 // First implements FragmentIterator. 67 func (i *filteringIter) First() *Span { 68 return i.filter(i.iter.First(), +1) 69 } 70 71 // Last implements FragmentIterator. 72 func (i *filteringIter) Last() *Span { 73 return i.filter(i.iter.Last(), -1) 74 } 75 76 // Next implements FragmentIterator. 77 func (i *filteringIter) Next() *Span { 78 return i.filter(i.iter.Next(), +1) 79 } 80 81 // Prev implements FragmentIterator. 82 func (i *filteringIter) Prev() *Span { 83 return i.filter(i.iter.Prev(), -1) 84 } 85 86 // Error implements FragmentIterator. 87 func (i *filteringIter) Error() error { 88 return i.iter.Error() 89 } 90 91 // Close implements FragmentIterator. 92 func (i *filteringIter) Close() error { 93 return i.iter.Close() 94 } 95 96 // filter uses the filterFn (if configured) to filter and possibly mutate the 97 // given Span. If the current Span is to be skipped, the iterator continues 98 // iterating in the given direction until it lands on a Span that should be 99 // returned, or the iterator becomes invalid. 100 func (i *filteringIter) filter(span *Span, dir int8) *Span { 101 if i.filterFn == nil { 102 return span 103 } 104 for i.Error() == nil && span != nil { 105 if keep := i.filterFn(span, &i.span); keep { 106 return &i.span 107 } 108 if dir == +1 { 109 span = i.iter.Next() 110 } else { 111 span = i.iter.Prev() 112 } 113 } 114 return span 115 }