Skip to content

Commit 37173f0

Browse files
committed
chore(docsfilter): merge multiple iterators into one
1 parent f85bbec commit 37173f0

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

docsfilter/merged_iterator.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package docsfilter
2+
3+
type TombstonesIterator interface {
4+
Next() (uint32, bool)
5+
}
6+
7+
func NewNMergedIterators(iterators []TombstonesIterator) TombstonesIterator {
8+
if len(iterators) == 0 {
9+
return &EmptyIterator{}
10+
}
11+
12+
if len(iterators) == 1 {
13+
return iterators[0]
14+
}
15+
16+
merged := NewMergedIterator(iterators[0], iterators[1])
17+
for _, s := range iterators[2:] {
18+
merged = NewMergedIterator(merged, s)
19+
}
20+
return merged
21+
}
22+
23+
type MergedIterator struct {
24+
a, b TombstonesIterator
25+
curA, curB uint32
26+
init bool
27+
}
28+
29+
func NewMergedIterator(
30+
a, b TombstonesIterator,
31+
) TombstonesIterator {
32+
return &MergedIterator{
33+
a: a,
34+
b: b,
35+
init: false,
36+
}
37+
}
38+
39+
func (it *MergedIterator) Next() (uint32, bool) {
40+
if !it.init {
41+
it.readA()
42+
it.readB()
43+
it.init = true
44+
}
45+
46+
if it.a == nil && it.b == nil {
47+
return 0, false
48+
}
49+
if it.a == nil {
50+
return it.readB(), true
51+
}
52+
if it.b == nil {
53+
return it.readA(), true
54+
}
55+
56+
if it.curA == it.curB {
57+
it.readA() // skip duplicate
58+
if it.a == nil {
59+
return it.readB(), true
60+
}
61+
}
62+
if it.curB < it.curA {
63+
return it.readB(), true
64+
}
65+
return it.readA(), true
66+
}
67+
68+
func (it *MergedIterator) readA() uint32 {
69+
var has bool
70+
current := it.curA
71+
72+
if it.curA, has = it.a.Next(); !has {
73+
it.a = nil // stop reading a
74+
}
75+
76+
return current
77+
}
78+
79+
func (it *MergedIterator) readB() uint32 {
80+
var has bool
81+
current := it.curB
82+
83+
if it.curB, has = it.b.Next(); !has {
84+
it.b = nil // stop reading b
85+
}
86+
87+
return current
88+
}
89+
90+
type EmptyIterator struct{}
91+
92+
func (it *EmptyIterator) Next() (uint32, bool) {
93+
return 0, false
94+
}

docsfilter/merged_iterator_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package docsfilter
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestMergedIterator(t *testing.T) {
10+
iterators := []TombstonesIterator{
11+
&testIterator{lids: []uint32{1, 2, 5, 22, 45}},
12+
&testIterator{lids: []uint32{2, 3, 9, 15, 33, 45}},
13+
&testIterator{lids: []uint32{1, 7, 8, 45}},
14+
}
15+
16+
mergedIterator := NewNMergedIterators(iterators)
17+
resLIDs := make([]uint32, 0)
18+
for {
19+
lid, has := mergedIterator.Next()
20+
if !has {
21+
break
22+
}
23+
resLIDs = append(resLIDs, lid)
24+
25+
}
26+
require.Equal(t, []uint32{1, 2, 3, 5, 7, 8, 9, 15, 22, 33, 45}, resLIDs)
27+
}
28+
29+
type testIterator struct {
30+
lids []uint32
31+
}
32+
33+
func (it *testIterator) Next() (uint32, bool) {
34+
if len(it.lids) == 0 {
35+
return 0, false
36+
}
37+
38+
lid := it.lids[0]
39+
it.lids = it.lids[1:]
40+
return lid, true
41+
}

0 commit comments

Comments
 (0)