forked from Kotlin/kotlinx.coroutines
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerate.kt
139 lines (121 loc) · 4.76 KB
/
generate.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package kotlinx.coroutines
import java.util.*
/**
* Creates a Sequence object based on received coroutine [c].
*
* Each call of yield suspend function within the coroutine lambda generates
* next element of resulting sequence. Calling yieldAll suspend function can be used to
* yield sequence of values efficiently.
*/
fun <T> generate(coroutine c: GeneratorController<T>.() -> Continuation<Unit>): Sequence<T> =
object : NestedIterable<T> {
override fun nestedIterator(): NestedIterator<T> {
val iterator = GeneratorController<T>()
iterator.setNextStep(iterator.c())
return iterator
}
}
class GeneratorController<T> internal constructor() : NestedIterator<T>() {
private lateinit var nextStep: Continuation<Unit>
override fun computeNextItemOrIterator() {
nextStep.resume(Unit)
}
internal fun setNextStep(step: Continuation<Unit>) {
nextStep = step
}
suspend fun yield(value: T, c: Continuation<Unit>) {
setNext(value)
setNextStep(c)
}
private fun yieldFromIterator(iterator: Iterator<T>, c: Continuation<Unit>) {
setNextIterator(iterator)
setNextStep(c)
}
suspend fun yieldAll(values: Sequence<T>, c: Continuation<Unit>) {
yieldFromIterator(if (values is NestedIterable<T>)
values.nestedIterator() else
values.iterator(), c)
}
suspend fun yieldAll(values: Iterable<T>, c: Continuation<Unit>) {
yieldFromIterator(values.iterator(), c)
}
suspend fun yieldAll(vararg values: T, c: Continuation<Unit>) {
yieldFromIterator(values.iterator(), c)
}
operator fun handleResult(result: Unit, c: Continuation<Nothing>) {
done()
}
}
/**
* Extends [AbstractIterator] adding it the ability to produce a next nested iterator instead of a next item.
* If [hasNext] is true, then either a next item or a next nested iterator is produced.
*
* If [nextNestedIterator] != null, the consumer should use the items from [nextNestedIterator]
* and then continue iterating over this iterator. Also if a next nested iterator is produced,
* the value returned by [next] should not be used as it is invalid, but [next] should still be called.
* Therefore [NestedIterator] cannot be used as a normal iterator.
*/
abstract class NestedIterator<T> internal constructor() : AbstractIterator<T>() {
/**
* Either `null` or the iterator that should be used before continuing iteration over this iterator.
* If [hasNext] is true and nextNestedIterator is null, then there's no next iterator.
*/
internal var nextNestedIterator: Iterator<T>? = null
private set
final override fun computeNext(): Unit {
nextNestedIterator = null
computeNextItemOrIterator()
}
/**
* Computes the next item or a next nested iterator of this iterator.
*
* This callback method should call one of these three methods:
*
* * [setNext] with the next value of the iteration
* * [setNextIterator] with the next nested iterator of the iteration
* * [done] to indicate there are no more elements
*
* Failure to call either method will result in the iteration terminating with a failed state
*/
internal abstract fun computeNextItemOrIterator()
protected fun setNextIterator(iterator: Iterator<T>) {
nextNestedIterator = iterator
setNext(null as T) //state transfer to Ready
}
}
/**
* Manages [NestedIterator]s, arranging them in a stack and switching between them as they produce next
* nested iterators or finish.
* The concept is taken from
* [this article](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/specsharp-iterators.pdf).
*/
private class RootIterator<T> constructor(iterator: Iterator<T>) : AbstractIterator<T>() {
private val stack = Stack<Iterator<T>>().apply { push(iterator) }
override fun computeNext() {
while (true) {
if (stack.isEmpty()) {
done()
return
}
val i = stack.peek()
if (!i.hasNext()) {
stack.pop()
} else {
if (i is NestedIterator<T> && i.nextNestedIterator != null) {
stack.push(i.nextNestedIterator)
i.next() //state transfer to NotReady
} else {
setNext(i.next())
return
}
}
}
}
}
/**
* Default implementation of [Sequence] for [NestedIterator] providers.
*/
private interface NestedIterable<T> : Sequence<T> {
fun nestedIterator(): NestedIterator<T>
override fun iterator(): Iterator<T> = RootIterator(nestedIterator())
}