Skip to content

Commit 8dc3fce

Browse files
committed
manifest: genericize btree iterator
Genericize the B-Tree iterator so it may be used with the upcoming blob file B-Tree.
1 parent df4fc02 commit 8dc3fce

File tree

3 files changed

+58
-52
lines changed

3 files changed

+58
-52
lines changed

internal/manifest/btree.go

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -776,8 +776,8 @@ func (t *btree[M]) Insert(item M) error {
776776
// tableMetadataIter returns a new iterator over a B-Tree of *TableMetadata. It
777777
// is not safe to continue using an iterator after modifications are made to the
778778
// tree. If modifications are made, create a new iterator.
779-
func tableMetadataIter(tree *btree[*TableMetadata]) iterator {
780-
return iterator{r: tree.root, pos: -1, cmp: tree.bcmp}
779+
func tableMetadataIter(tree *btree[*TableMetadata]) iterator[*TableMetadata] {
780+
return iterator[*TableMetadata]{r: tree.root, pos: -1, cmp: tree.bcmp}
781781
}
782782

783783
// Count returns the number of files contained within the B-Tree.
@@ -821,28 +821,28 @@ func (n *node[M]) writeString(b *strings.Builder) {
821821

822822
// iterStack represents a stack of (node, pos) tuples, which captures
823823
// iteration state as an iterator descends a btree of TableMetadata.
824-
type iterStack struct {
824+
type iterStack[M fileMetadata] struct {
825825
// a contains aLen stack frames when an iterator stack is short enough.
826826
// If the iterator stack overflows the capacity of iterStackArr, the stack
827827
// is moved to s and aLen is set to -1.
828-
a iterStackArr
828+
a iterStackArr[M]
829829
aLen int16 // -1 when using s
830-
s []iterFrame
830+
s []iterFrame[M]
831831
}
832832

833833
// Used to avoid allocations for stacks below a certain size.
834-
type iterStackArr [3]iterFrame
834+
type iterStackArr[M fileMetadata] [3]iterFrame[M]
835835

836-
type iterFrame struct {
837-
n *node[*TableMetadata]
836+
type iterFrame[M fileMetadata] struct {
837+
n *node[M]
838838
pos int16
839839
}
840840

841-
func (is *iterStack) push(f iterFrame) {
841+
func (is *iterStack[M]) push(f iterFrame[M]) {
842842
if is.aLen == -1 {
843843
is.s = append(is.s, f)
844844
} else if int(is.aLen) == len(is.a) {
845-
is.s = make([]iterFrame, int(is.aLen)+1, 2*int(is.aLen))
845+
is.s = make([]iterFrame[M], int(is.aLen)+1, 2*int(is.aLen))
846846
copy(is.s, is.a[:])
847847
is.s[int(is.aLen)] = f
848848
is.aLen = -1
@@ -852,7 +852,7 @@ func (is *iterStack) push(f iterFrame) {
852852
}
853853
}
854854

855-
func (is *iterStack) pop() iterFrame {
855+
func (is *iterStack[M]) pop() iterFrame[M] {
856856
if is.aLen == -1 {
857857
f := is.s[len(is.s)-1]
858858
is.s = is.s[:len(is.s)-1]
@@ -862,26 +862,26 @@ func (is *iterStack) pop() iterFrame {
862862
return is.a[is.aLen]
863863
}
864864

865-
func (is *iterStack) len() int {
865+
func (is *iterStack[M]) len() int {
866866
if is.aLen == -1 {
867867
return len(is.s)
868868
}
869869
return int(is.aLen)
870870
}
871871

872-
func (is *iterStack) clone() iterStack {
872+
func (is *iterStack[M]) clone() iterStack[M] {
873873
// If the iterator is using the embedded iterStackArr, we only need to
874874
// copy the struct itself.
875875
if is.s == nil {
876876
return *is
877877
}
878878
clone := *is
879-
clone.s = make([]iterFrame, len(is.s))
879+
clone.s = make([]iterFrame[M], len(is.s))
880880
copy(clone.s, is.s)
881881
return clone
882882
}
883883

884-
func (is *iterStack) nth(n int) (f iterFrame, ok bool) {
884+
func (is *iterStack[M]) nth(n int) (f iterFrame[M], ok bool) {
885885
if is.aLen == -1 {
886886
if n >= len(is.s) {
887887
return f, false
@@ -894,7 +894,7 @@ func (is *iterStack) nth(n int) (f iterFrame, ok bool) {
894894
return is.a[n], true
895895
}
896896

897-
func (is *iterStack) reset() {
897+
func (is *iterStack[M]) reset() {
898898
if is.aLen == -1 {
899899
is.s = is.s[:0]
900900
} else {
@@ -903,26 +903,26 @@ func (is *iterStack) reset() {
903903
}
904904

905905
// an iterator provides search and traversal within a btree of *TableMetadata.
906-
type iterator struct {
906+
type iterator[M fileMetadata] struct {
907907
// the root node of the B-Tree.
908-
r *node[*TableMetadata]
908+
r *node[M]
909909
// n and pos make up the current position of the iterator.
910910
// If valid, n.items[pos] is the current value of the iterator.
911911
//
912912
// n may be nil iff i.r is nil.
913-
n *node[*TableMetadata]
913+
n *node[M]
914914
pos int16
915915
// cmp dictates the ordering of the TableMetadata.
916-
cmp func(*TableMetadata, *TableMetadata) int
916+
cmp func(M, M) int
917917
// a stack of n's ancestors within the B-Tree, alongside the position
918918
// taken to arrive at n. If non-empty, the bottommost frame of the stack
919919
// will always contain the B-Tree root.
920-
s iterStack
920+
s iterStack[M]
921921
}
922922

923923
// countLeft returns the count of files that are to the left of the current
924924
// iterator position.
925-
func (i *iterator) countLeft() int {
925+
func (i *iterator[M]) countLeft() int {
926926
if i.r == nil {
927927
return 0
928928
}
@@ -973,19 +973,19 @@ func (i *iterator) countLeft() int {
973973
return count
974974
}
975975

976-
func (i *iterator) clone() iterator {
976+
func (i *iterator[M]) clone() iterator[M] {
977977
c := *i
978978
c.s = i.s.clone()
979979
return c
980980
}
981981

982-
func (i *iterator) reset() {
982+
func (i *iterator[M]) reset() {
983983
i.n = i.r
984984
i.pos = -1
985985
i.s.reset()
986986
}
987987

988-
func (i iterator) String() string {
988+
func (i iterator[M]) String() string {
989989
var buf bytes.Buffer
990990
for n := 0; ; n++ {
991991
f, ok := i.s.nth(n)
@@ -1002,7 +1002,7 @@ func (i iterator) String() string {
10021002
return buf.String()
10031003
}
10041004

1005-
func cmpIter(a, b iterator) int {
1005+
func cmpIter[M fileMetadata](a, b iterator[M]) int {
10061006
if a.r != b.r {
10071007
panic("compared iterators from different btrees")
10081008
}
@@ -1044,7 +1044,7 @@ func cmpIter(a, b iterator) int {
10441044
// end sentinel state which sorts after everything else.
10451045
var aok, bok bool
10461046
for i := 0; ; i++ {
1047-
var af, bf iterFrame
1047+
var af, bf iterFrame[M]
10481048
af, aok = a.s.nth(i)
10491049
bf, bok = b.s.nth(i)
10501050
if !aok || !bok {
@@ -1095,15 +1095,15 @@ func cmpIter(a, b iterator) int {
10951095
}
10961096
}
10971097

1098-
func (i *iterator) descend(n *node[*TableMetadata], pos int16) {
1099-
i.s.push(iterFrame{n: n, pos: pos})
1098+
func (i *iterator[M]) descend(n *node[M], pos int16) {
1099+
i.s.push(iterFrame[M]{n: n, pos: pos})
11001100
i.n = n.children[pos]
11011101
i.pos = 0
11021102
}
11031103

11041104
// ascend ascends up to the current node's parent and resets the position
11051105
// to the one previously set for this parent node.
1106-
func (i *iterator) ascend() {
1106+
func (i *iterator[M]) ascend() {
11071107
f := i.s.pop()
11081108
i.n = f.n
11091109
i.pos = f.pos
@@ -1112,7 +1112,7 @@ func (i *iterator) ascend() {
11121112
// find seeks the iterator to the provided table metadata if it exists in the
11131113
// tree. It returns true if the table metadata is found and false otherwise. If
11141114
// find returns false, the position of the iterator is undefined.
1115-
func (i *iterator) find(m *TableMetadata) bool {
1115+
func (i *iterator[M]) find(m M) bool {
11161116
i.reset()
11171117
if i.r == nil {
11181118
return false
@@ -1131,7 +1131,7 @@ func (i *iterator) find(m *TableMetadata) bool {
11311131
}
11321132

11331133
// first seeks to the first item in the btree.
1134-
func (i *iterator) first() {
1134+
func (i *iterator[M]) first() {
11351135
i.reset()
11361136
if i.r == nil {
11371137
return
@@ -1143,7 +1143,7 @@ func (i *iterator) first() {
11431143
}
11441144

11451145
// last seeks to the last item in the btree.
1146-
func (i *iterator) last() {
1146+
func (i *iterator[M]) last() {
11471147
i.reset()
11481148
if i.r == nil {
11491149
return
@@ -1156,7 +1156,7 @@ func (i *iterator) last() {
11561156

11571157
// next positions the iterator to the item immediately following
11581158
// its current position.
1159-
func (i *iterator) next() {
1159+
func (i *iterator[M]) next() {
11601160
if i.r == nil {
11611161
return
11621162
}
@@ -1183,7 +1183,7 @@ func (i *iterator) next() {
11831183

11841184
// prev positions the iterator to the item immediately preceding
11851185
// its current position.
1186-
func (i *iterator) prev() {
1186+
func (i *iterator[M]) prev() {
11871187
if i.r == nil {
11881188
return
11891189
}
@@ -1208,13 +1208,13 @@ func (i *iterator) prev() {
12081208
}
12091209

12101210
// valid returns whether the iterator is positioned at a valid position.
1211-
func (i *iterator) valid() bool {
1211+
func (i *iterator[M]) valid() bool {
12121212
return i.r != nil && i.pos >= 0 && i.pos < i.n.count
12131213
}
12141214

12151215
// cur returns the table metadata at the iterator's current position. It is
12161216
// illegal to call cur if the iterator is not valid.
1217-
func (i *iterator) cur() *TableMetadata {
1217+
func (i *iterator[M]) cur() M {
12181218
if invariants.Enabled && !i.valid() {
12191219
panic("btree iterator.cur invoked on invalid iterator")
12201220
}

internal/manifest/btree_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ func keyWithMemo(i int, memo map[int]InternalKey) InternalKey {
160160
return s
161161
}
162162

163-
func checkIterRelative(t *testing.T, it *iterator, start, end int, keyMemo map[int]InternalKey) {
163+
func checkIterRelative(
164+
t *testing.T, it *iterator[*TableMetadata], start, end int, keyMemo map[int]InternalKey,
165+
) {
164166
t.Helper()
165167
i := start
166168
for ; it.valid(); it.next() {
@@ -176,7 +178,9 @@ func checkIterRelative(t *testing.T, it *iterator, start, end int, keyMemo map[i
176178
}
177179
}
178180

179-
func checkIter(t *testing.T, it iterator, start, end int, keyMemo map[int]InternalKey) {
181+
func checkIter(
182+
t *testing.T, it iterator[*TableMetadata], start, end int, keyMemo map[int]InternalKey,
183+
) {
180184
t.Helper()
181185
i := start
182186
for it.first(); it.valid(); it.next() {
@@ -501,11 +505,11 @@ func TestBTreeCloneConcurrentOperations(t *testing.T) {
501505

502506
// TestIterStack tests the interface of the iterStack type.
503507
func TestIterStack(t *testing.T) {
504-
f := func(i int) iterFrame {
505-
return iterFrame{pos: int16(i)}
508+
f := func(i int) iterFrame[*TableMetadata] {
509+
return iterFrame[*TableMetadata]{pos: int16(i)}
506510
}
507-
var is iterStack
508-
for i := 1; i <= 2*len(iterStackArr{}); i++ {
511+
var is iterStack[*TableMetadata]
512+
for i := 1; i <= 2*len(iterStackArr[*TableMetadata]{}); i++ {
509513
var j int
510514
for j = 0; j < i; j++ {
511515
is.push(f(j))

internal/manifest/level_metadata.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func NewLevelSliceSpecificOrder(files []*TableMetadata) LevelSlice {
208208
}
209209

210210
// newLevelSlice constructs a new LevelSlice backed by iter.
211-
func newLevelSlice(iter iterator) LevelSlice {
211+
func newLevelSlice(iter iterator[*TableMetadata]) LevelSlice {
212212
s := LevelSlice{iter: iter}
213213
if iter.r != nil {
214214
s.length = iter.r.subtreeCount
@@ -221,7 +221,9 @@ func newLevelSlice(iter iterator) LevelSlice {
221221
// by the provided start and end bounds. The provided startBound and endBound
222222
// iterators must be iterators over the same B-Tree. Both start and end bounds
223223
// are inclusive.
224-
func newBoundedLevelSlice(iter iterator, startBound, endBound *iterator) LevelSlice {
224+
func newBoundedLevelSlice(
225+
iter iterator[*TableMetadata], startBound, endBound *iterator[*TableMetadata],
226+
) LevelSlice {
225227
s := LevelSlice{
226228
iter: iter,
227229
start: startBound,
@@ -253,13 +255,13 @@ func newBoundedLevelSlice(iter iterator, startBound, endBound *iterator) LevelSl
253255
// LevelSlices should be constructed through one of the existing constructors,
254256
// not manually initialized.
255257
type LevelSlice struct {
256-
iter iterator
258+
iter iterator[*TableMetadata]
257259
length int
258260
// start and end form the inclusive bounds of a slice of files within a
259261
// level of the LSM. They may be nil if the entire B-Tree backing iter is
260262
// accessible.
261-
start *iterator
262-
end *iterator
263+
start *iterator[*TableMetadata]
264+
end *iterator[*TableMetadata]
263265
}
264266

265267
func (ls LevelSlice) verifyInvariants() {
@@ -430,11 +432,11 @@ const (
430432
// LevelIterator iterates over a set of files' metadata. Its zero value is an
431433
// empty iterator.
432434
type LevelIterator struct {
433-
iter iterator
435+
iter iterator[*TableMetadata]
434436
// If set, start is an inclusive lower bound on the iterator.
435-
start *iterator
437+
start *iterator[*TableMetadata]
436438
// If set, end is an inclusive upper bound on the iterator.
437-
end *iterator
439+
end *iterator[*TableMetadata]
438440
filter KeyType
439441
}
440442

@@ -508,7 +510,7 @@ func (i *LevelIterator) Filter(keyType KeyType) LevelIterator {
508510
return l
509511
}
510512

511-
func emptyWithBounds(i iterator, start, end *iterator) bool {
513+
func emptyWithBounds(i iterator[*TableMetadata], start, end *iterator[*TableMetadata]) bool {
512514
// If i.r is nil, the iterator was constructed from an empty btree.
513515
// If the end bound is before the start bound, the bounds represent an
514516
// empty slice of the B-Tree.

0 commit comments

Comments
 (0)