github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/keyspan/interleaving_iter_test.go (about) 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 "bytes" 9 "fmt" 10 "io" 11 "sort" 12 "strings" 13 "testing" 14 15 "github.com/stretchr/testify/require" 16 "github.com/zuoyebang/bitalostable/internal/base" 17 "github.com/zuoyebang/bitalostable/internal/datadriven" 18 "github.com/zuoyebang/bitalostable/internal/testkeys" 19 ) 20 21 func TestInterleavingIter(t *testing.T) { 22 runInterleavingIterTest(t, "testdata/interleaving_iter") 23 } 24 25 func TestInterleavingIter_Masking(t *testing.T) { 26 runInterleavingIterTest(t, "testdata/interleaving_iter_masking") 27 } 28 29 type maskingHooks struct { 30 log io.Writer 31 cmp base.Compare 32 split base.Split 33 threshold []byte 34 maskSuffix []byte 35 } 36 37 func (m *maskingHooks) SpanChanged(s *Span) { 38 if m.log != nil { 39 if s == nil { 40 fmt.Fprintln(m.log, "-- SpanChanged(nil)") 41 } else { 42 fmt.Fprintf(m.log, "-- SpanChanged(%s)\n", s) 43 } 44 } 45 46 // Find the smallest suffix of a key contained within the Span, excluding 47 // suffixes less than m.threshold. 48 m.maskSuffix = nil 49 if s == nil || m.threshold == nil || len(s.Keys) == 0 { 50 return 51 } 52 for i := range s.Keys { 53 if s.Keys[i].Suffix == nil { 54 continue 55 } 56 if m.cmp(s.Keys[i].Suffix, m.threshold) < 0 { 57 continue 58 } 59 if m.maskSuffix == nil || m.cmp(m.maskSuffix, s.Keys[i].Suffix) > 0 { 60 m.maskSuffix = s.Keys[i].Suffix 61 } 62 } 63 } 64 65 func (m *maskingHooks) SkipPoint(userKey []byte) bool { 66 pointSuffix := userKey[m.split(userKey):] 67 return m.maskSuffix != nil && len(pointSuffix) > 0 && m.cmp(m.maskSuffix, pointSuffix) < 0 68 } 69 70 func runInterleavingIterTest(t *testing.T, filename string) { 71 cmp := testkeys.Comparer.Compare 72 var keyspanIter MergingIter 73 var pointIter pointIterator 74 var iter InterleavingIter 75 var buf bytes.Buffer 76 hooks := maskingHooks{ 77 log: &buf, 78 cmp: testkeys.Comparer.Compare, 79 split: testkeys.Comparer.Split, 80 } 81 82 formatKey := func(k *base.InternalKey, v []byte) { 83 if k == nil { 84 fmt.Fprint(&buf, ".") 85 return 86 } 87 s := iter.Span() 88 fmt.Fprintf(&buf, "PointKey: %s\n", k.String()) 89 if s != nil { 90 fmt.Fprintf(&buf, "Span: %s\n-", s) 91 } else { 92 fmt.Fprintf(&buf, "Span: %s\n-", Span{}) 93 } 94 } 95 96 datadriven.RunTest(t, filename, func(td *datadriven.TestData) string { 97 buf.Reset() 98 switch td.Cmd { 99 case "set-masking-threshold": 100 hooks.threshold = []byte(strings.TrimSpace(td.Input)) 101 return "OK" 102 case "define-rangekeys": 103 var spans []Span 104 lines := strings.Split(strings.TrimSpace(td.Input), "\n") 105 for _, line := range lines { 106 spans = append(spans, ParseSpan(line)) 107 } 108 keyspanIter.Init(cmp, noopTransform, NewIter(cmp, spans)) 109 hooks.maskSuffix = nil 110 iter.Init(testkeys.Comparer, &pointIter, &keyspanIter, &hooks, nil, nil) 111 return "OK" 112 case "define-pointkeys": 113 var points []base.InternalKey 114 lines := strings.Split(strings.TrimSpace(td.Input), "\n") 115 for _, line := range lines { 116 points = append(points, base.ParseInternalKey(line)) 117 } 118 pointIter = pointIterator{cmp: cmp, keys: points} 119 hooks.maskSuffix = nil 120 iter.Init(testkeys.Comparer, &pointIter, &keyspanIter, &hooks, nil, nil) 121 return "OK" 122 case "iter": 123 buf.Reset() 124 // Clear any previous bounds. 125 iter.SetBounds(nil, nil) 126 lines := strings.Split(strings.TrimSpace(td.Input), "\n") 127 for _, line := range lines { 128 bufLen := buf.Len() 129 line = strings.TrimSpace(line) 130 i := strings.IndexByte(line, ' ') 131 iterCmd := line 132 if i > 0 { 133 iterCmd = string(line[:i]) 134 } 135 switch iterCmd { 136 case "first": 137 formatKey(iter.First()) 138 case "last": 139 formatKey(iter.Last()) 140 case "next": 141 formatKey(iter.Next()) 142 case "prev": 143 formatKey(iter.Prev()) 144 case "seek-ge": 145 formatKey(iter.SeekGE([]byte(strings.TrimSpace(line[i:])), base.SeekGEFlagsNone)) 146 case "seek-prefix-ge": 147 key := []byte(strings.TrimSpace(line[i:])) 148 prefix := key[:testkeys.Comparer.Split(key)] 149 formatKey(iter.SeekPrefixGE(prefix, key, base.SeekGEFlagsNone)) 150 case "seek-lt": 151 formatKey(iter.SeekLT([]byte(strings.TrimSpace(line[i:])), base.SeekLTFlagsNone)) 152 case "set-bounds": 153 bounds := strings.Fields(line[i:]) 154 if len(bounds) != 2 { 155 return fmt.Sprintf("set-bounds expects 2 bounds, got %d", len(bounds)) 156 } 157 l, u := []byte(bounds[0]), []byte(bounds[1]) 158 if bounds[0] == "." { 159 l = nil 160 } 161 if bounds[1] == "." { 162 u = nil 163 } 164 iter.SetBounds(l, u) 165 default: 166 return fmt.Sprintf("unrecognized iter command %q", iterCmd) 167 } 168 require.NoError(t, iter.Error()) 169 if buf.Len() > bufLen { 170 fmt.Fprintln(&buf) 171 } 172 } 173 return strings.TrimSpace(buf.String()) 174 default: 175 return fmt.Sprintf("unrecognized command %q", td.Cmd) 176 } 177 }) 178 } 179 180 type pointIterator struct { 181 cmp base.Compare 182 keys []base.InternalKey 183 lower []byte 184 upper []byte 185 index int 186 } 187 188 var _ base.InternalIterator = &pointIterator{} 189 190 func (i *pointIterator) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKey, []byte) { 191 i.index = sort.Search(len(i.keys), func(j int) bool { 192 return i.cmp(i.keys[j].UserKey, key) >= 0 193 }) 194 if i.index < 0 || i.index >= len(i.keys) { 195 return nil, nil 196 } 197 if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 { 198 return nil, nil 199 } 200 return &i.keys[i.index], nil 201 } 202 203 func (i *pointIterator) SeekPrefixGE( 204 prefix, key []byte, flags base.SeekGEFlags, 205 ) (*base.InternalKey, []byte) { 206 return i.SeekGE(key, flags) 207 } 208 209 func (i *pointIterator) SeekLT(key []byte, flags base.SeekLTFlags) (*base.InternalKey, []byte) { 210 i.index = sort.Search(len(i.keys), func(j int) bool { 211 return i.cmp(i.keys[j].UserKey, key) >= 0 212 }) 213 i.index-- 214 if i.index < 0 || i.index >= len(i.keys) { 215 return nil, nil 216 } 217 if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 { 218 return nil, nil 219 } 220 return &i.keys[i.index], nil 221 } 222 223 func (i *pointIterator) First() (*base.InternalKey, []byte) { 224 i.index = 0 225 if i.index < 0 || i.index >= len(i.keys) { 226 return nil, nil 227 } 228 if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 { 229 return nil, nil 230 } 231 return &i.keys[i.index], nil 232 } 233 234 func (i *pointIterator) Last() (*base.InternalKey, []byte) { 235 i.index = len(i.keys) - 1 236 if i.index < 0 || i.index >= len(i.keys) { 237 return nil, nil 238 } 239 if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 { 240 return nil, nil 241 } 242 return &i.keys[i.index], nil 243 } 244 245 func (i *pointIterator) Next() (*base.InternalKey, []byte) { 246 i.index++ 247 if i.index < 0 || i.index >= len(i.keys) { 248 return nil, nil 249 } 250 if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 { 251 return nil, nil 252 } 253 return &i.keys[i.index], nil 254 } 255 256 func (i *pointIterator) Prev() (*base.InternalKey, []byte) { 257 i.index-- 258 if i.index < 0 || i.index >= len(i.keys) { 259 return nil, nil 260 } 261 if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 { 262 return nil, nil 263 } 264 return &i.keys[i.index], nil 265 } 266 267 func (i *pointIterator) Close() error { return nil } 268 func (i *pointIterator) Error() error { return nil } 269 func (i *pointIterator) String() string { return "test-point-iterator" } 270 func (i *pointIterator) SetBounds(lower, upper []byte) { 271 i.lower, i.upper = lower, upper 272 }