github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/internal/manifest/level_metadata.go (about) 1 // Copyright 2020 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 manifest 6 7 import ( 8 "bytes" 9 "fmt" 10 11 "github.com/cockroachdb/pebble/internal/base" 12 "github.com/cockroachdb/pebble/internal/invariants" 13 ) 14 15 // LevelMetadata contains metadata for all of the files within 16 // a level of the LSM. 17 type LevelMetadata struct { 18 level int 19 totalSize uint64 20 // NumVirtual is the number of virtual sstables in the level. 21 NumVirtual uint64 22 // VirtualSize is the size of the virtual sstables in the level. 23 VirtualSize uint64 24 tree btree 25 } 26 27 // clone makes a copy of the level metadata, implicitly increasing the ref 28 // count of every file contained within lm. 29 func (lm *LevelMetadata) clone() LevelMetadata { 30 return LevelMetadata{ 31 level: lm.level, 32 totalSize: lm.totalSize, 33 NumVirtual: lm.NumVirtual, 34 VirtualSize: lm.VirtualSize, 35 tree: lm.tree.Clone(), 36 } 37 } 38 39 func (lm *LevelMetadata) release() (obsolete []*FileBacking) { 40 return lm.tree.Release() 41 } 42 43 func makeLevelMetadata(cmp Compare, level int, files []*FileMetadata) LevelMetadata { 44 bcmp := btreeCmpSeqNum 45 if level > 0 { 46 bcmp = btreeCmpSmallestKey(cmp) 47 } 48 var lm LevelMetadata 49 lm.level = level 50 lm.tree, _ = makeBTree(bcmp, files) 51 for _, f := range files { 52 lm.totalSize += f.Size 53 if f.Virtual { 54 lm.NumVirtual++ 55 lm.VirtualSize += f.Size 56 } 57 } 58 return lm 59 } 60 61 func makeBTree(cmp btreeCmp, files []*FileMetadata) (btree, LevelSlice) { 62 var t btree 63 t.cmp = cmp 64 for _, f := range files { 65 t.Insert(f) 66 } 67 return t, newLevelSlice(t.Iter()) 68 } 69 70 func (lm *LevelMetadata) insert(f *FileMetadata) error { 71 if err := lm.tree.Insert(f); err != nil { 72 return err 73 } 74 lm.totalSize += f.Size 75 if f.Virtual { 76 lm.NumVirtual++ 77 lm.VirtualSize += f.Size 78 } 79 return nil 80 } 81 82 func (lm *LevelMetadata) remove(f *FileMetadata) bool { 83 lm.totalSize -= f.Size 84 if f.Virtual { 85 lm.NumVirtual-- 86 lm.VirtualSize -= f.Size 87 } 88 return lm.tree.Delete(f) 89 } 90 91 // Empty indicates whether there are any files in the level. 92 func (lm *LevelMetadata) Empty() bool { 93 return lm.tree.Count() == 0 94 } 95 96 // Len returns the number of files within the level. 97 func (lm *LevelMetadata) Len() int { 98 return lm.tree.Count() 99 } 100 101 // Size returns the cumulative size of all the files within the level. 102 func (lm *LevelMetadata) Size() uint64 { 103 return lm.totalSize 104 } 105 106 // Iter constructs a LevelIterator over the entire level. 107 func (lm *LevelMetadata) Iter() LevelIterator { 108 return LevelIterator{iter: lm.tree.Iter()} 109 } 110 111 // Slice constructs a slice containing the entire level. 112 func (lm *LevelMetadata) Slice() LevelSlice { 113 return newLevelSlice(lm.tree.Iter()) 114 } 115 116 // Find finds the provided file in the level if it exists. 117 func (lm *LevelMetadata) Find(cmp base.Compare, m *FileMetadata) *LevelFile { 118 iter := lm.Iter() 119 if lm.level != 0 { 120 // If lm holds files for levels >0, we can narrow our search by binary 121 // searching by bounds. 122 o := overlaps(iter, cmp, m.Smallest.UserKey, 123 m.Largest.UserKey, m.Largest.IsExclusiveSentinel()) 124 iter = o.Iter() 125 } 126 for f := iter.First(); f != nil; f = iter.Next() { 127 if f == m { 128 lf := iter.Take() 129 return &lf 130 } 131 } 132 return nil 133 } 134 135 // Annotation lazily calculates and returns the annotation defined by 136 // Annotator. The Annotator is used as the key for pre-calculated 137 // values, so equal Annotators must be used to avoid duplicate computations 138 // and cached annotations. Annotation must not be called concurrently, and in 139 // practice this is achieved by requiring callers to hold DB.mu. 140 func (lm *LevelMetadata) Annotation(annotator Annotator) interface{} { 141 if lm.Empty() { 142 return annotator.Zero(nil) 143 } 144 v, _ := lm.tree.root.Annotation(annotator) 145 return v 146 } 147 148 // InvalidateAnnotation clears any cached annotations defined by Annotator. The 149 // Annotator is used as the key for pre-calculated values, so equal Annotators 150 // must be used to clear the appropriate cached annotation. InvalidateAnnotation 151 // must not be called concurrently, and in practice this is achieved by 152 // requiring callers to hold DB.mu. 153 func (lm *LevelMetadata) InvalidateAnnotation(annotator Annotator) { 154 if lm.Empty() { 155 return 156 } 157 lm.tree.root.InvalidateAnnotation(annotator) 158 } 159 160 // LevelFile holds a file's metadata along with its position 161 // within a level of the LSM. 162 type LevelFile struct { 163 *FileMetadata 164 slice LevelSlice 165 } 166 167 // Slice constructs a LevelSlice containing only this file. 168 func (lf LevelFile) Slice() LevelSlice { 169 return lf.slice 170 } 171 172 // NewLevelSliceSeqSorted constructs a LevelSlice over the provided files, 173 // sorted by the L0 sequence number sort order. 174 // TODO(jackson): Can we improve this interface or avoid needing to export 175 // a slice constructor like this? 176 func NewLevelSliceSeqSorted(files []*FileMetadata) LevelSlice { 177 tr, slice := makeBTree(btreeCmpSeqNum, files) 178 tr.Release() 179 slice.verifyInvariants() 180 return slice 181 } 182 183 // NewLevelSliceKeySorted constructs a LevelSlice over the provided files, 184 // sorted by the files smallest keys. 185 // TODO(jackson): Can we improve this interface or avoid needing to export 186 // a slice constructor like this? 187 func NewLevelSliceKeySorted(cmp base.Compare, files []*FileMetadata) LevelSlice { 188 tr, slice := makeBTree(btreeCmpSmallestKey(cmp), files) 189 tr.Release() 190 slice.verifyInvariants() 191 return slice 192 } 193 194 // NewLevelSliceSpecificOrder constructs a LevelSlice over the provided files, 195 // ordering the files by their order in the provided slice. It's used in 196 // tests. 197 // TODO(jackson): Update tests to avoid requiring this and remove it. 198 func NewLevelSliceSpecificOrder(files []*FileMetadata) LevelSlice { 199 tr, slice := makeBTree(btreeCmpSpecificOrder(files), files) 200 tr.Release() 201 slice.verifyInvariants() 202 return slice 203 } 204 205 // newLevelSlice constructs a new LevelSlice backed by iter. 206 func newLevelSlice(iter iterator) LevelSlice { 207 s := LevelSlice{iter: iter} 208 if iter.r != nil { 209 s.length = iter.r.subtreeCount 210 } 211 s.verifyInvariants() 212 return s 213 } 214 215 // newBoundedLevelSlice constructs a new LevelSlice backed by iter and bounded 216 // by the provided start and end bounds. The provided startBound and endBound 217 // iterators must be iterators over the same B-Tree. Both start and end bounds 218 // are inclusive. 219 func newBoundedLevelSlice(iter iterator, startBound, endBound *iterator) LevelSlice { 220 s := LevelSlice{ 221 iter: iter, 222 start: startBound, 223 end: endBound, 224 } 225 if iter.valid() { 226 s.length = endBound.countLeft() - startBound.countLeft() 227 // NB: The +1 is a consequence of the end bound being inclusive. 228 if endBound.valid() { 229 s.length++ 230 } 231 // NB: A slice that's empty due to its bounds may have an endBound 232 // positioned before the startBound due to the inclusive bounds. 233 // TODO(jackson): Consider refactoring the end boundary to be exclusive; 234 // it would simplify some areas (eg, here) and complicate others (eg, 235 // Reslice-ing to grow compactions). 236 if s.length < 0 { 237 s.length = 0 238 } 239 } 240 s.verifyInvariants() 241 return s 242 } 243 244 // LevelSlice contains a slice of the files within a level of the LSM. 245 // A LevelSlice is immutable once created, but may be used to construct a 246 // mutable LevelIterator over the slice's files. 247 // 248 // LevelSlices should be constructed through one of the existing constructors, 249 // not manually initialized. 250 type LevelSlice struct { 251 iter iterator 252 length int 253 // start and end form the inclusive bounds of a slice of files within a 254 // level of the LSM. They may be nil if the entire B-Tree backing iter is 255 // accessible. 256 start *iterator 257 end *iterator 258 } 259 260 func (ls LevelSlice) verifyInvariants() { 261 if invariants.Enabled { 262 i := ls.Iter() 263 var length int 264 for f := i.First(); f != nil; f = i.Next() { 265 length++ 266 } 267 if ls.length != length { 268 panic(fmt.Sprintf("LevelSlice %s has length %d value; actual length is %d", ls, ls.length, length)) 269 } 270 } 271 } 272 273 // Each invokes fn for each element in the slice. 274 func (ls LevelSlice) Each(fn func(*FileMetadata)) { 275 iter := ls.Iter() 276 for f := iter.First(); f != nil; f = iter.Next() { 277 fn(f) 278 } 279 } 280 281 // String implements fmt.Stringer. 282 func (ls LevelSlice) String() string { 283 var buf bytes.Buffer 284 fmt.Fprintf(&buf, "%d files: ", ls.length) 285 ls.Each(func(f *FileMetadata) { 286 if buf.Len() > 0 { 287 fmt.Fprintf(&buf, " ") 288 } 289 fmt.Fprint(&buf, f) 290 }) 291 return buf.String() 292 } 293 294 // Empty indicates whether the slice contains any files. 295 func (ls *LevelSlice) Empty() bool { 296 return emptyWithBounds(ls.iter, ls.start, ls.end) 297 } 298 299 // Iter constructs a LevelIterator that iterates over the slice. 300 func (ls *LevelSlice) Iter() LevelIterator { 301 return LevelIterator{ 302 start: ls.start, 303 end: ls.end, 304 iter: ls.iter.clone(), 305 } 306 } 307 308 // Len returns the number of files in the slice. Its runtime is constant. 309 func (ls *LevelSlice) Len() int { 310 return ls.length 311 } 312 313 // SizeSum sums the size of all files in the slice. Its runtime is linear in 314 // the length of the slice. 315 func (ls *LevelSlice) SizeSum() uint64 { 316 var sum uint64 317 iter := ls.Iter() 318 for f := iter.First(); f != nil; f = iter.Next() { 319 sum += f.Size 320 } 321 return sum 322 } 323 324 // NumVirtual returns the number of virtual sstables in the level. Its runtime is 325 // linear in the length of the slice. 326 func (ls *LevelSlice) NumVirtual() uint64 { 327 var n uint64 328 iter := ls.Iter() 329 for f := iter.First(); f != nil; f = iter.Next() { 330 if f.Virtual { 331 n++ 332 } 333 } 334 return n 335 } 336 337 // VirtualSizeSum returns the sum of the sizes of the virtual sstables in the 338 // level. 339 func (ls *LevelSlice) VirtualSizeSum() uint64 { 340 var sum uint64 341 iter := ls.Iter() 342 for f := iter.First(); f != nil; f = iter.Next() { 343 if f.Virtual { 344 sum += f.Size 345 } 346 } 347 return sum 348 } 349 350 // Reslice constructs a new slice backed by the same underlying level, with 351 // new start and end positions. Reslice invokes the provided function, passing 352 // two LevelIterators: one positioned to i's inclusive start and one 353 // positioned to i's inclusive end. The resliceFunc may move either iterator 354 // forward or backwards, including beyond the callee's original bounds to 355 // capture additional files from the underlying level. Reslice constructs and 356 // returns a new LevelSlice with the final bounds of the iterators after 357 // calling resliceFunc. 358 func (ls LevelSlice) Reslice(resliceFunc func(start, end *LevelIterator)) LevelSlice { 359 if ls.iter.r == nil { 360 return ls 361 } 362 var start, end LevelIterator 363 if ls.start == nil { 364 start.iter = ls.iter.clone() 365 start.iter.first() 366 } else { 367 start.iter = ls.start.clone() 368 } 369 if ls.end == nil { 370 end.iter = ls.iter.clone() 371 end.iter.last() 372 } else { 373 end.iter = ls.end.clone() 374 } 375 resliceFunc(&start, &end) 376 return newBoundedLevelSlice(start.iter.clone(), &start.iter, &end.iter) 377 } 378 379 // KeyType is used to specify the type of keys we're looking for in 380 // LevelIterator positioning operations. Files not containing any keys of the 381 // desired type are skipped. 382 type KeyType int8 383 384 const ( 385 // KeyTypePointAndRange denotes a search among the entire keyspace, including 386 // both point keys and range keys. No sstables are skipped. 387 KeyTypePointAndRange KeyType = iota 388 // KeyTypePoint denotes a search among the point keyspace. SSTables with no 389 // point keys will be skipped. Note that the point keyspace includes rangedels. 390 KeyTypePoint 391 // KeyTypeRange denotes a search among the range keyspace. SSTables with no 392 // range keys will be skipped. 393 KeyTypeRange 394 ) 395 396 type keyTypeAnnotator struct{} 397 398 var _ Annotator = keyTypeAnnotator{} 399 400 func (k keyTypeAnnotator) Zero(dst interface{}) interface{} { 401 var val *KeyType 402 if dst != nil { 403 val = dst.(*KeyType) 404 } else { 405 val = new(KeyType) 406 } 407 *val = KeyTypePoint 408 return val 409 } 410 411 func (k keyTypeAnnotator) Accumulate(m *FileMetadata, dst interface{}) (interface{}, bool) { 412 v := dst.(*KeyType) 413 switch *v { 414 case KeyTypePoint: 415 if m.HasRangeKeys { 416 *v = KeyTypePointAndRange 417 } 418 case KeyTypePointAndRange: 419 // Do nothing. 420 default: 421 panic("unexpected key type") 422 } 423 return v, true 424 } 425 426 func (k keyTypeAnnotator) Merge(src interface{}, dst interface{}) interface{} { 427 v := dst.(*KeyType) 428 srcVal := src.(*KeyType) 429 switch *v { 430 case KeyTypePoint: 431 if *srcVal == KeyTypePointAndRange { 432 *v = KeyTypePointAndRange 433 } 434 case KeyTypePointAndRange: 435 // Do nothing. 436 default: 437 panic("unexpected key type") 438 } 439 return v 440 } 441 442 // LevelIterator iterates over a set of files' metadata. Its zero value is an 443 // empty iterator. 444 type LevelIterator struct { 445 iter iterator 446 start *iterator 447 end *iterator 448 filter KeyType 449 } 450 451 func (i LevelIterator) String() string { 452 var buf bytes.Buffer 453 iter := i.iter.clone() 454 iter.first() 455 iter.prev() 456 if i.iter.pos == -1 { 457 fmt.Fprint(&buf, "(<start>)*") 458 } 459 iter.next() 460 for ; iter.valid(); iter.next() { 461 if buf.Len() > 0 { 462 fmt.Fprint(&buf, " ") 463 } 464 465 if i.start != nil && cmpIter(iter, *i.start) == 0 { 466 fmt.Fprintf(&buf, " [ ") 467 } 468 isCurrentPos := cmpIter(iter, i.iter) == 0 469 if isCurrentPos { 470 fmt.Fprint(&buf, " ( ") 471 } 472 fmt.Fprint(&buf, iter.cur().String()) 473 if isCurrentPos { 474 fmt.Fprint(&buf, " )*") 475 } 476 if i.end != nil && cmpIter(iter, *i.end) == 0 { 477 fmt.Fprintf(&buf, " ]") 478 } 479 } 480 if i.iter.n != nil && i.iter.pos >= i.iter.n.count { 481 if buf.Len() > 0 { 482 fmt.Fprint(&buf, " ") 483 } 484 fmt.Fprint(&buf, "(<end>)*") 485 } 486 return buf.String() 487 } 488 489 // Clone copies the iterator, returning an independent iterator at the same 490 // position. 491 func (i *LevelIterator) Clone() LevelIterator { 492 if i.iter.r == nil { 493 return *i 494 } 495 // The start and end iterators are not cloned and are treated as 496 // immutable. 497 return LevelIterator{ 498 iter: i.iter.clone(), 499 start: i.start, 500 end: i.end, 501 filter: i.filter, 502 } 503 } 504 505 // Current returns the item at the current iterator position. 506 // 507 // Current is deprecated. Callers should instead use the return value of a 508 // positioning operation. 509 func (i *LevelIterator) Current() *FileMetadata { 510 if !i.iter.valid() || 511 (i.end != nil && cmpIter(i.iter, *i.end) > 0) || 512 (i.start != nil && cmpIter(i.iter, *i.start) < 0) { 513 return nil 514 } 515 return i.iter.cur() 516 } 517 518 func (i *LevelIterator) empty() bool { 519 return emptyWithBounds(i.iter, i.start, i.end) 520 } 521 522 // Filter clones the iterator and sets the desired KeyType as the key to filter 523 // files on. 524 func (i *LevelIterator) Filter(keyType KeyType) LevelIterator { 525 l := i.Clone() 526 l.filter = keyType 527 return l 528 } 529 530 func emptyWithBounds(i iterator, start, end *iterator) bool { 531 // If i.r is nil, the iterator was constructed from an empty btree. 532 // If the end bound is before the start bound, the bounds represent an 533 // empty slice of the B-Tree. 534 return i.r == nil || (start != nil && end != nil && cmpIter(*end, *start) < 0) 535 } 536 537 // First seeks to the first file in the iterator and returns it. 538 func (i *LevelIterator) First() *FileMetadata { 539 if i.empty() { 540 return nil 541 } 542 if i.start != nil { 543 i.iter = i.start.clone() 544 } else { 545 i.iter.first() 546 } 547 if !i.iter.valid() { 548 return nil 549 } 550 return i.skipFilteredForward(i.iter.cur()) 551 } 552 553 // Last seeks to the last file in the iterator and returns it. 554 func (i *LevelIterator) Last() *FileMetadata { 555 if i.empty() { 556 return nil 557 } 558 if i.end != nil { 559 i.iter = i.end.clone() 560 } else { 561 i.iter.last() 562 } 563 if !i.iter.valid() { 564 return nil 565 } 566 return i.skipFilteredBackward(i.iter.cur()) 567 } 568 569 // Next advances the iterator to the next file and returns it. 570 func (i *LevelIterator) Next() *FileMetadata { 571 if i.iter.r == nil { 572 return nil 573 } 574 if invariants.Enabled && (i.iter.pos >= i.iter.n.count || (i.end != nil && cmpIter(i.iter, *i.end) > 0)) { 575 panic("pebble: cannot next forward-exhausted iterator") 576 } 577 i.iter.next() 578 if !i.iter.valid() { 579 return nil 580 } 581 return i.skipFilteredForward(i.iter.cur()) 582 } 583 584 // Prev moves the iterator the previous file and returns it. 585 func (i *LevelIterator) Prev() *FileMetadata { 586 if i.iter.r == nil { 587 return nil 588 } 589 if invariants.Enabled && (i.iter.pos < 0 || (i.start != nil && cmpIter(i.iter, *i.start) < 0)) { 590 panic("pebble: cannot prev backward-exhausted iterator") 591 } 592 i.iter.prev() 593 if !i.iter.valid() { 594 return nil 595 } 596 return i.skipFilteredBackward(i.iter.cur()) 597 } 598 599 // SeekGE seeks to the first file in the iterator's file set with a largest 600 // user key greater than or equal to the provided user key. The iterator must 601 // have been constructed from L1+, because it requires the underlying files to 602 // be sorted by user keys and non-overlapping. 603 func (i *LevelIterator) SeekGE(cmp Compare, userKey []byte) *FileMetadata { 604 // TODO(jackson): Assert that i.iter.cmp == btreeCmpSmallestKey. 605 if i.iter.r == nil { 606 return nil 607 } 608 m := i.seek(func(m *FileMetadata) bool { 609 return cmp(m.Largest.UserKey, userKey) >= 0 610 }) 611 if i.filter != KeyTypePointAndRange && m != nil { 612 b, ok := m.LargestBound(i.filter) 613 if !ok { 614 m = i.Next() 615 } else if c := cmp(b.UserKey, userKey); c < 0 || c == 0 && b.IsExclusiveSentinel() { 616 // This file does not contain any keys of the type ≥ lower. It 617 // should be filtered, even though it does contain point keys. 618 m = i.Next() 619 } 620 } 621 return i.skipFilteredForward(m) 622 } 623 624 // SeekLT seeks to the last file in the iterator's file set with a smallest 625 // user key less than the provided user key. The iterator must have been 626 // constructed from L1+, because it requires the underlying files to be sorted 627 // by user keys and non-overlapping. 628 func (i *LevelIterator) SeekLT(cmp Compare, userKey []byte) *FileMetadata { 629 // TODO(jackson): Assert that i.iter.cmp == btreeCmpSmallestKey. 630 if i.iter.r == nil { 631 return nil 632 } 633 i.seek(func(m *FileMetadata) bool { 634 return cmp(m.Smallest.UserKey, userKey) >= 0 635 }) 636 m := i.Prev() 637 // Although i.Prev() guarantees that the current file contains keys of the 638 // relevant type, it doesn't guarantee that the keys of the relevant type 639 // are < userKey. 640 if i.filter != KeyTypePointAndRange && m != nil { 641 b, ok := m.SmallestBound(i.filter) 642 if !ok { 643 panic("unreachable") 644 } 645 if c := cmp(b.UserKey, userKey); c >= 0 { 646 // This file does not contain any keys of the type ≥ lower. It 647 // should be filtered, even though it does contain point keys. 648 m = i.Prev() 649 } 650 } 651 return i.skipFilteredBackward(m) 652 } 653 654 // skipFilteredForward takes the file metadata at the iterator's current 655 // position, and skips forward if the current key-type filter (i.filter) 656 // excludes the file. It skips until it finds an unfiltered file or exhausts the 657 // level. If lower is != nil, skipFilteredForward skips any files that do not 658 // contain keys with the provided key-type ≥ lower. 659 // 660 // skipFilteredForward also enforces the upper bound, returning nil if at any 661 // point the upper bound is exceeded. 662 func (i *LevelIterator) skipFilteredForward(meta *FileMetadata) *FileMetadata { 663 for meta != nil && !meta.ContainsKeyType(i.filter) { 664 i.iter.next() 665 if !i.iter.valid() { 666 meta = nil 667 } else { 668 meta = i.iter.cur() 669 } 670 } 671 if meta != nil && i.end != nil && cmpIter(i.iter, *i.end) > 0 { 672 // Exceeded upper bound. 673 meta = nil 674 } 675 return meta 676 } 677 678 // skipFilteredBackward takes the file metadata at the iterator's current 679 // position, and skips backward if the current key-type filter (i.filter) 680 // excludes the file. It skips until it finds an unfiltered file or exhausts the 681 // level. If upper is != nil, skipFilteredBackward skips any files that do not 682 // contain keys with the provided key-type < upper. 683 // 684 // skipFilteredBackward also enforces the lower bound, returning nil if at any 685 // point the lower bound is exceeded. 686 func (i *LevelIterator) skipFilteredBackward(meta *FileMetadata) *FileMetadata { 687 for meta != nil && !meta.ContainsKeyType(i.filter) { 688 i.iter.prev() 689 if !i.iter.valid() { 690 meta = nil 691 } else { 692 meta = i.iter.cur() 693 } 694 } 695 if meta != nil && i.start != nil && cmpIter(i.iter, *i.start) < 0 { 696 // Exceeded lower bound. 697 meta = nil 698 } 699 return meta 700 } 701 702 func (i *LevelIterator) seek(fn func(*FileMetadata) bool) *FileMetadata { 703 i.iter.seek(fn) 704 705 // i.iter.seek seeked in the unbounded underlying B-Tree. If the iterator 706 // has start or end bounds, we may have exceeded them. Reset to the bounds 707 // if necessary. 708 // 709 // NB: The LevelIterator and LevelSlice semantics require that a bounded 710 // LevelIterator/LevelSlice containing files x0, x1, ..., xn behave 711 // identically to an unbounded LevelIterator/LevelSlice of a B-Tree 712 // containing x0, x1, ..., xn. In other words, any files outside the 713 // LevelIterator's bounds should not influence the iterator's behavior. 714 // When seeking, this means a SeekGE that seeks beyond the end bound, 715 // followed by a Prev should return the last element within bounds. 716 if i.end != nil && cmpIter(i.iter, *i.end) > 0 { 717 i.iter = i.end.clone() 718 // Since seek(fn) positioned beyond i.end, we know there is nothing to 719 // return within bounds. 720 i.iter.next() 721 return nil 722 } else if i.start != nil && cmpIter(i.iter, *i.start) < 0 { 723 i.iter = i.start.clone() 724 } 725 if !i.iter.valid() { 726 return nil 727 } 728 return i.iter.cur() 729 } 730 731 // Take constructs a LevelFile containing the file at the iterator's current 732 // position. Take panics if the iterator is not currently positioned over a 733 // file. 734 func (i *LevelIterator) Take() LevelFile { 735 m := i.Current() 736 if m == nil { 737 panic("Take called on invalid LevelIterator") 738 } 739 // LevelSlice's start and end fields are immutable and are positioned to 740 // the same position for a LevelFile because they're inclusive, so we can 741 // share one iterator stack between the two bounds. 742 boundsIter := i.iter.clone() 743 s := newBoundedLevelSlice(i.iter.clone(), &boundsIter, &boundsIter) 744 return LevelFile{ 745 FileMetadata: m, 746 slice: s, 747 } 748 }