Skip to content

Commit f9dc61a

Browse files
committed
Fix iterator semantics. Improve treap traversal.
1 parent 8f0f1e4 commit f9dc61a

9 files changed

+208
-68
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func main() {
165165
// To do this, we use an iterator. Contrary to treaps, iterators are stateful and
166166
// mutable! As such, they are NOT thread-safe. However, multiple concurrent
167167
// iterators can traverse the same treap safely.
168-
for iterator := handle.Iter(root); iterator.Next(); {
168+
for iterator := handle.Iter(root); iterator.Node != nil; iterator.Next(); {
169169
fmt.Printf("%s %s: %d\n", iterator.Key, iterator.Value, iterator.Weight)
170170
}
171171
}

comparator_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
)
1010

1111
func TestMaxTreap(t *testing.T) {
12+
t.Parallel()
13+
1214
comp := treap.MaxTreap(treap.IntComparator)
1315

1416
for _, tc := range []struct {
@@ -38,6 +40,8 @@ func TestMaxTreap(t *testing.T) {
3840
}
3941

4042
func TestIntComparator(t *testing.T) {
43+
t.Parallel()
44+
4145
for _, tc := range []struct {
4246
desc string
4347
test []interface{}
@@ -68,6 +72,8 @@ func TestIntComparator(t *testing.T) {
6872
}
6973

7074
func TestInt8Comparator(t *testing.T) {
75+
t.Parallel()
76+
7177
for _, tc := range []struct {
7278
desc string
7379
test []interface{}
@@ -98,6 +104,8 @@ func TestInt8Comparator(t *testing.T) {
98104
}
99105

100106
func TestInt16Comparator(t *testing.T) {
107+
t.Parallel()
108+
101109
for _, tc := range []struct {
102110
desc string
103111
test []interface{}
@@ -128,6 +136,8 @@ func TestInt16Comparator(t *testing.T) {
128136
}
129137

130138
func TestInt32Comparator(t *testing.T) {
139+
t.Parallel()
140+
131141
for _, tc := range []struct {
132142
desc string
133143
test []interface{}
@@ -158,6 +168,8 @@ func TestInt32Comparator(t *testing.T) {
158168
}
159169

160170
func TestInt64Comparator(t *testing.T) {
171+
t.Parallel()
172+
161173
for _, tc := range []struct {
162174
desc string
163175
test []interface{}
@@ -188,6 +200,8 @@ func TestInt64Comparator(t *testing.T) {
188200
}
189201

190202
func TestStringComparator(t *testing.T) {
203+
t.Parallel()
204+
191205
for _, tc := range []struct {
192206
desc string
193207
test []interface{}
@@ -221,6 +235,8 @@ func TestStringComparator(t *testing.T) {
221235
}
222236

223237
func TestByteComparator(t *testing.T) {
238+
t.Parallel()
239+
224240
for _, tc := range []struct {
225241
desc string
226242
test []interface{}
@@ -254,6 +270,7 @@ func TestByteComparator(t *testing.T) {
254270
}
255271

256272
func TestTimeComparator(t *testing.T) {
273+
t.Parallel()
257274

258275
t0 := time.Now()
259276

@@ -284,6 +301,8 @@ func TestTimeComparator(t *testing.T) {
284301
}
285302

286303
func TestUIntComparator(t *testing.T) {
304+
t.Parallel()
305+
287306
for _, tc := range []struct {
288307
desc string
289308
test []interface{}
@@ -314,6 +333,8 @@ func TestUIntComparator(t *testing.T) {
314333
}
315334

316335
func TestUInt8Comparator(t *testing.T) {
336+
t.Parallel()
337+
317338
for _, tc := range []struct {
318339
desc string
319340
test []interface{}
@@ -344,6 +365,8 @@ func TestUInt8Comparator(t *testing.T) {
344365
}
345366

346367
func TestUInt16Comparator(t *testing.T) {
368+
t.Parallel()
369+
347370
for _, tc := range []struct {
348371
desc string
349372
test []interface{}
@@ -374,6 +397,8 @@ func TestUInt16Comparator(t *testing.T) {
374397
}
375398

376399
func TestUInt32Comparator(t *testing.T) {
400+
t.Parallel()
401+
377402
for _, tc := range []struct {
378403
desc string
379404
test []interface{}
@@ -404,6 +429,8 @@ func TestUInt32Comparator(t *testing.T) {
404429
}
405430

406431
func TestUInt64Comparator(t *testing.T) {
432+
t.Parallel()
433+
407434
for _, tc := range []struct {
408435
desc string
409436
test []interface{}
@@ -434,6 +461,8 @@ func TestUInt64Comparator(t *testing.T) {
434461
}
435462

436463
func TestFloat32Comparator(t *testing.T) {
464+
t.Parallel()
465+
437466
for _, tc := range []struct {
438467
desc string
439468
test []interface{}
@@ -464,6 +493,8 @@ func TestFloat32Comparator(t *testing.T) {
464493
}
465494

466495
func TestFloat64Comparator(t *testing.T) {
496+
t.Parallel()
497+
467498
for _, tc := range []struct {
468499
desc string
469500
test []interface{}

handle.go

+3-40
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,9 @@ func (h Handle) Pop(n *Node) (interface{}, *Node) {
214214

215215
// Iter walks the tree in key-order.
216216
func (h Handle) Iter(n *Node) *Iterator {
217-
return &Iterator{
218-
n: n,
219-
stack: make([]*Node, 0, 16),
220-
}
217+
it := &Iterator{stack: push(nil, n)}
218+
it.Next()
219+
return it
221220
}
222221

223222
func (h Handle) leftRotation(n *Node) *Node {
@@ -251,39 +250,3 @@ func (h Handle) rightRotation(n *Node) *Node {
251250
n.Right.Right,
252251
)
253252
}
254-
255-
// Iterator contains treap iteration state. Its methods are NOT thread-safe, but
256-
// multiple concurrent iterators are supported.
257-
type Iterator struct {
258-
*Node
259-
n *Node
260-
stack []*Node
261-
}
262-
263-
// Next item.
264-
func (it *Iterator) Next() (more bool) {
265-
for more = it.More(); more; {
266-
if it.n != nil {
267-
it.stack = append(it.stack, it.n)
268-
it.n = it.n.Left
269-
continue
270-
}
271-
272-
it.n, it.stack = pop(it.stack)
273-
it.Node = it.n
274-
it.n = it.n.Right
275-
276-
break
277-
}
278-
279-
return
280-
}
281-
282-
// More returns true if the iterator has not traversed the entire treap.
283-
func (it *Iterator) More() bool {
284-
return len(it.stack) > 0 || it.n != nil
285-
}
286-
287-
func pop(stack []*Node) (*Node, []*Node) {
288-
return stack[len(stack)-1], stack[:len(stack)-1]
289-
}

example_test.go renamed to handle_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"github.com/lthibault/treap"
77
)
88

9-
func ExampleUsage() {
9+
func ExampleHandle() {
1010
// Treap operations are performed by a lightweight handle. Usually, you'll create a
1111
// single global handle and share it between goroutines. Handle's methods are thread-
1212
// safe.
@@ -96,8 +96,9 @@ func ExampleUsage() {
9696
// iterators can traverse the same treap safely.
9797
var i int
9898
fmt.Println("\n[ binary search-tree traversal (notice keys are sorted alphabetically)... ]")
99-
for iterator := handle.Iter(root); iterator.Next(); i++ {
99+
for iterator := handle.Iter(root); iterator.Node != nil; iterator.Next() {
100100
fmt.Printf("[%d] %s %s: %d\n", i, iterator.Key, iterator.Value, iterator.Weight)
101+
i++
101102
}
102103

103104
// Output:

iterator.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package treap
2+
3+
// Iterator contains treap iteration state. Its methods are NOT thread-safe, but
4+
// multiple concurrent iterators are supported.
5+
type Iterator struct {
6+
*Node
7+
stack *stack
8+
}
9+
10+
// Next item.
11+
func (it *Iterator) Next() {
12+
// are we resuming?
13+
if it.Node != nil {
14+
it.Node = it.Node.Right
15+
} else {
16+
it.Node, it.stack = pop(it.stack)
17+
}
18+
19+
for {
20+
if it.Node == nil {
21+
it.Node, it.stack = pop(it.stack)
22+
break
23+
}
24+
25+
it.stack = push(it.stack, it.Node)
26+
it.Node = it.Node.Left
27+
}
28+
}
29+
30+
// Stack is a singly-linked list of nodes.
31+
type stack struct {
32+
*Node
33+
next *stack
34+
}
35+
36+
func push(s *stack, n *Node) *stack {
37+
if n == nil {
38+
return s
39+
}
40+
41+
return &stack{
42+
Node: n,
43+
next: s,
44+
}
45+
}
46+
47+
func pop(s *stack) (*Node, *stack) {
48+
if s == nil {
49+
return nil, nil
50+
}
51+
52+
return s.Node, s.next
53+
}

iterator_test.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package treap_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/lthibault/treap"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestIter_Empty(t *testing.T) {
12+
t.Parallel()
13+
14+
var empty *treap.Node
15+
assert.Nil(t, handle.Iter(empty).Node,
16+
"iterator for empty root should have nil node")
17+
18+
var i int
19+
for it := handle.Iter(empty); it.Node != nil; it.Next() {
20+
i++
21+
}
22+
assert.Zero(t, i, "empty iterator shoudl not loop")
23+
}
24+
25+
func TestIter_SingleEntry(t *testing.T) {
26+
t.Parallel()
27+
28+
var root = &treap.Node{
29+
Key: 1,
30+
Value: 1,
31+
Weight: 1,
32+
}
33+
34+
assert.NotNil(t, handle.Iter(root).Node,
35+
"iterator for non-empty root should have non-nil node")
36+
37+
var i int
38+
for it := handle.Iter(root); it.Node != nil; it.Next() {
39+
i++
40+
}
41+
assert.Equal(t, 1, i, "iterator should loop one time")
42+
}
43+
44+
func TestIter_MultiEntry(t *testing.T) {
45+
t.Parallel()
46+
47+
var (
48+
root *treap.Node
49+
ok bool
50+
tt = []treap.Node{
51+
{
52+
Key: 0,
53+
Value: 0,
54+
Weight: 0,
55+
},
56+
{
57+
Key: 1,
58+
Value: 1,
59+
Weight: 1,
60+
},
61+
{
62+
Key: 2,
63+
Value: 2,
64+
Weight: 2,
65+
},
66+
{
67+
Key: 3,
68+
Value: 3,
69+
Weight: 2,
70+
},
71+
{
72+
Key: 4,
73+
Value: 4,
74+
Weight: 2,
75+
},
76+
{
77+
Key: 5,
78+
Value: 5,
79+
Weight: 1,
80+
},
81+
{
82+
Key: 6,
83+
Value: 6,
84+
Weight: 2,
85+
},
86+
}
87+
)
88+
89+
for _, n := range tt {
90+
root, ok = handle.Insert(root, n.Key, n.Value, n.Weight)
91+
require.True(t, ok, "precondition failed: insert must succeed")
92+
}
93+
94+
var ns []treap.Node
95+
for it := handle.Iter(root); it.Node != nil; it.Next() {
96+
t.Log(it.Node.Key)
97+
ns = append(ns, *it.Node)
98+
}
99+
assert.Len(t, ns, len(tt), "iterator should loop %d times", len(tt))
100+
101+
for i, n := range tt {
102+
assert.Equal(t, n.Key, ns[i].Key, "iterator should traverse in key order")
103+
}
104+
}

treap_bench_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func BenchmarkIterSync(b *testing.B) {
166166
b.ResetTimer()
167167

168168
for i := 0; i < b.N; i++ {
169-
for it := handle.Iter(root); it.Next(); {
169+
for it := handle.Iter(root); it.Node != nil; it.Next() {
170170
discardNode = it.Node
171171
}
172172

treap_race_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build race
12
// +build race
23

34
package treap_test

0 commit comments

Comments
 (0)