Skip to content

Commit 37b41b6

Browse files
committed
Add Stepper.scala and ArraySeq.scala in unchanged form to stdlib
1 parent e33fc7d commit 37b41b6

File tree

2 files changed

+715
-0
lines changed

2 files changed

+715
-0
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.collection
14+
15+
import java.util.function.{Consumer, DoubleConsumer, IntConsumer, LongConsumer}
16+
import java.util.{PrimitiveIterator, Spliterator, Iterator => JIterator}
17+
import java.{lang => jl}
18+
19+
import scala.collection.Stepper.EfficientSplit
20+
21+
/** Steppers exist to enable creating Java streams over Scala collections, see
22+
* [[scala.jdk.StreamConverters]]. Besides that use case, they allow iterating over collections
23+
* holding unboxed primitives (e.g., `Array[Int]`) without boxing the elements.
24+
*
25+
* Steppers have an iterator-like interface with methods `hasStep` and `nextStep()`. The difference
26+
* to iterators - and the reason `Stepper` is not a subtype of `Iterator` - is that there are
27+
* hand-specialized variants of `Stepper` for `Int`, `Long` and `Double` ([[IntStepper]], etc.).
28+
* These enable iterating over collections holding unboxed primitives (e.g., Arrays,
29+
* [[scala.jdk.Accumulator]]s) without boxing the elements.
30+
*
31+
* The selection of primitive types (`Int`, `Long` and `Double`) matches the hand-specialized
32+
* variants of Java Streams ([[java.util.stream.Stream]], [[java.util.stream.IntStream]], etc.)
33+
* and the corresponding Java Spliterators ([[java.util.Spliterator]], [[java.util.Spliterator.OfInt]], etc.).
34+
*
35+
* Steppers can be converted to Scala Iterators, Java Iterators and Java Spliterators. Primitive
36+
* Steppers are converted to the corresponding primitive Java Iterators and Spliterators.
37+
*
38+
* @tparam A the element type of the Stepper
39+
*/
40+
trait Stepper[@specialized(Double, Int, Long) +A] {
41+
/** Check if there's an element available. */
42+
def hasStep: Boolean
43+
44+
/** Return the next element and advance the stepper */
45+
def nextStep(): A
46+
47+
/** Split this stepper, if applicable. The elements of the current Stepper are split up between
48+
* the resulting Stepper and the current stepper.
49+
*
50+
* May return `null`, in which case the current Stepper yields the same elements as before.
51+
*
52+
* See method `trySplit` in [[java.util.Spliterator]].
53+
*/
54+
def trySplit(): Stepper[A]
55+
56+
/** Returns an estimate of the number of elements of this Stepper, or [[Long.MaxValue]]. See
57+
* method `estimateSize` in [[java.util.Spliterator]].
58+
*/
59+
def estimateSize: Long
60+
61+
/** Returns a set of characteristics of this Stepper and its elements. See method
62+
* `characteristics` in [[java.util.Spliterator]].
63+
*/
64+
def characteristics: Int
65+
66+
/** Returns a [[java.util.Spliterator]] corresponding to this Stepper.
67+
*
68+
* Note that the return type is `Spliterator[_]` instead of `Spliterator[A]` to allow returning
69+
* a [[java.util.Spliterator.OfInt]] (which is a `Spliterator[Integer]`) in the subclass [[IntStepper]]
70+
* (which is a `Stepper[Int]`).
71+
*/
72+
def spliterator[B >: A]: Spliterator[_]
73+
74+
/** Returns a Java [[java.util.Iterator]] corresponding to this Stepper.
75+
*
76+
* Note that the return type is `Iterator[_]` instead of `Iterator[A]` to allow returning
77+
* a [[java.util.PrimitiveIterator.OfInt]] (which is a `Iterator[Integer]`) in the subclass
78+
* [[IntStepper]] (which is a `Stepper[Int]`).
79+
*/
80+
def javaIterator[B >: A]: JIterator[_]
81+
82+
/** Returns an [[Iterator]] corresponding to this Stepper. Note that Iterators corresponding to
83+
* primitive Steppers box the elements.
84+
*/
85+
def iterator: Iterator[A] = new AbstractIterator[A] {
86+
def hasNext: Boolean = hasStep
87+
def next(): A = nextStep()
88+
}
89+
}
90+
91+
object Stepper {
92+
/** A marker trait that indicates that a `Stepper` can call `trySplit` with at worst O(log N) time
93+
* and space complexity, and that the division is likely to be reasonably even. Steppers marked
94+
* with `EfficientSplit` can be converted to parallel streams with the `asJavaParStream` method
95+
* defined in [[scala.jdk.StreamConverters]].
96+
*/
97+
trait EfficientSplit
98+
99+
private[collection] final def throwNSEE(): Nothing = throw new NoSuchElementException("Empty Stepper")
100+
101+
/* These adapter classes can wrap an AnyStepper of a numeric type into a possibly widened primitive Stepper type.
102+
* This provides a basis for more efficient stream processing on unboxed values provided that the original source
103+
* of the data is boxed. In other cases native implementations of the primitive stepper types should be provided
104+
* (see for example IntArrayStepper and WidenedByteArrayStepper). */
105+
106+
private[collection] class UnboxingDoubleStepper(st: AnyStepper[Double]) extends DoubleStepper {
107+
def hasStep: Boolean = st.hasStep
108+
def nextStep(): Double = st.nextStep()
109+
def estimateSize: Long = st.estimateSize
110+
def characteristics: Int = st.characteristics
111+
def trySplit(): DoubleStepper = {
112+
val s = st.trySplit()
113+
if (s == null) null else new UnboxingDoubleStepper(s)
114+
}
115+
}
116+
117+
private[collection] class UnboxingIntStepper(st: AnyStepper[Int]) extends IntStepper {
118+
def hasStep: Boolean = st.hasStep
119+
def nextStep(): Int = st.nextStep()
120+
def estimateSize: Long = st.estimateSize
121+
def characteristics: Int = st.characteristics
122+
def trySplit(): IntStepper = {
123+
val s = st.trySplit()
124+
if (s == null) null else new UnboxingIntStepper(s)
125+
}
126+
}
127+
128+
private[collection] class UnboxingLongStepper(st: AnyStepper[Long]) extends LongStepper {
129+
def hasStep: Boolean = st.hasStep
130+
def nextStep(): Long = st.nextStep()
131+
def estimateSize: Long = st.estimateSize
132+
def characteristics: Int = st.characteristics
133+
def trySplit(): LongStepper = {
134+
val s = st.trySplit()
135+
if (s == null) null else new UnboxingLongStepper(s)
136+
}
137+
}
138+
139+
private[collection] class UnboxingByteStepper(st: AnyStepper[Byte]) extends IntStepper {
140+
def hasStep: Boolean = st.hasStep
141+
def nextStep(): Int = st.nextStep()
142+
def estimateSize: Long = st.estimateSize
143+
def characteristics: Int = st.characteristics
144+
def trySplit(): IntStepper = {
145+
val s = st.trySplit()
146+
if (s == null) null else new UnboxingByteStepper(s)
147+
}
148+
}
149+
150+
private[collection] class UnboxingCharStepper(st: AnyStepper[Char]) extends IntStepper {
151+
def hasStep: Boolean = st.hasStep
152+
def nextStep(): Int = st.nextStep()
153+
def estimateSize: Long = st.estimateSize
154+
def characteristics: Int = st.characteristics
155+
def trySplit(): IntStepper = {
156+
val s = st.trySplit()
157+
if (s == null) null else new UnboxingCharStepper(s)
158+
}
159+
}
160+
161+
private[collection] class UnboxingShortStepper(st: AnyStepper[Short]) extends IntStepper {
162+
def hasStep: Boolean = st.hasStep
163+
def nextStep(): Int = st.nextStep()
164+
def estimateSize: Long = st.estimateSize
165+
def characteristics: Int = st.characteristics
166+
def trySplit(): IntStepper = {
167+
val s = st.trySplit()
168+
if (s == null) null else new UnboxingShortStepper(s)
169+
}
170+
}
171+
172+
private[collection] class UnboxingFloatStepper(st: AnyStepper[Float]) extends DoubleStepper {
173+
def hasStep: Boolean = st.hasStep
174+
def nextStep(): Double = st.nextStep()
175+
def estimateSize: Long = st.estimateSize
176+
def characteristics: Int = st.characteristics
177+
def trySplit(): DoubleStepper = {
178+
val s = st.trySplit()
179+
if (s == null) null else new UnboxingFloatStepper(s)
180+
}
181+
}
182+
}
183+
184+
/** A Stepper for arbitrary element types. See [[Stepper]]. */
185+
trait AnyStepper[+A] extends Stepper[A] {
186+
def trySplit(): AnyStepper[A]
187+
188+
def spliterator[B >: A]: Spliterator[B] = new AnyStepper.AnyStepperSpliterator(this)
189+
190+
def javaIterator[B >: A]: JIterator[B] = new JIterator[B] {
191+
def hasNext: Boolean = hasStep
192+
def next(): B = nextStep()
193+
}
194+
}
195+
196+
object AnyStepper {
197+
class AnyStepperSpliterator[A](s: AnyStepper[A]) extends Spliterator[A] {
198+
def tryAdvance(c: Consumer[_ >: A]): Boolean =
199+
if (s.hasStep) { c.accept(s.nextStep()); true } else false
200+
def trySplit(): Spliterator[A] = {
201+
val sp = s.trySplit()
202+
if (sp == null) null else sp.spliterator
203+
}
204+
def estimateSize(): Long = s.estimateSize
205+
def characteristics(): Int = s.characteristics
206+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
207+
override def forEachRemaining(c: Consumer[_ >: A]): Unit =
208+
while (s.hasStep) { c.accept(s.nextStep()) }
209+
}
210+
211+
def ofSeqDoubleStepper(st: DoubleStepper): AnyStepper[Double] = new BoxedDoubleStepper(st)
212+
def ofParDoubleStepper(st: DoubleStepper with EfficientSplit): AnyStepper[Double] with EfficientSplit = new BoxedDoubleStepper(st) with EfficientSplit
213+
214+
def ofSeqIntStepper(st: IntStepper): AnyStepper[Int] = new BoxedIntStepper(st)
215+
def ofParIntStepper(st: IntStepper with EfficientSplit): AnyStepper[Int] with EfficientSplit = new BoxedIntStepper(st) with EfficientSplit
216+
217+
def ofSeqLongStepper(st: LongStepper): AnyStepper[Long] = new BoxedLongStepper(st)
218+
def ofParLongStepper(st: LongStepper with EfficientSplit): AnyStepper[Long] with EfficientSplit = new BoxedLongStepper(st) with EfficientSplit
219+
220+
private[collection] class BoxedDoubleStepper(st: DoubleStepper) extends AnyStepper[Double] {
221+
def hasStep: Boolean = st.hasStep
222+
def nextStep(): Double = st.nextStep()
223+
def estimateSize: Long = st.estimateSize
224+
def characteristics: Int = st.characteristics
225+
def trySplit(): AnyStepper[Double] = {
226+
val s = st.trySplit()
227+
if (s == null) null else new BoxedDoubleStepper(s)
228+
}
229+
}
230+
231+
private[collection] class BoxedIntStepper(st: IntStepper) extends AnyStepper[Int] {
232+
def hasStep: Boolean = st.hasStep
233+
def nextStep(): Int = st.nextStep()
234+
def estimateSize: Long = st.estimateSize
235+
def characteristics: Int = st.characteristics
236+
def trySplit(): AnyStepper[Int] = {
237+
val s = st.trySplit()
238+
if (s == null) null else new BoxedIntStepper(s)
239+
}
240+
}
241+
242+
private[collection] class BoxedLongStepper(st: LongStepper) extends AnyStepper[Long] {
243+
def hasStep: Boolean = st.hasStep
244+
def nextStep(): Long = st.nextStep()
245+
def estimateSize: Long = st.estimateSize
246+
def characteristics: Int = st.characteristics
247+
def trySplit(): AnyStepper[Long] = {
248+
val s = st.trySplit()
249+
if (s == null) null else new BoxedLongStepper(s)
250+
}
251+
}
252+
}
253+
254+
/** A Stepper for Ints. See [[Stepper]]. */
255+
trait IntStepper extends Stepper[Int] {
256+
def trySplit(): IntStepper
257+
258+
def spliterator[B >: Int]: Spliterator.OfInt = new IntStepper.IntStepperSpliterator(this)
259+
260+
def javaIterator[B >: Int]: PrimitiveIterator.OfInt = new PrimitiveIterator.OfInt {
261+
def hasNext: Boolean = hasStep
262+
def nextInt(): Int = nextStep()
263+
}
264+
}
265+
object IntStepper {
266+
class IntStepperSpliterator(s: IntStepper) extends Spliterator.OfInt {
267+
def tryAdvance(c: IntConsumer): Boolean =
268+
if (s.hasStep) { c.accept(s.nextStep()); true } else false
269+
// Override for efficiency: don't wrap the function and call the `tryAdvance` overload
270+
override def tryAdvance(c: Consumer[_ >: jl.Integer]): Boolean = (c: AnyRef) match {
271+
case ic: IntConsumer => tryAdvance(ic)
272+
case _ => if (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())); true } else false
273+
}
274+
// override required for dotty#6152
275+
override def trySplit(): Spliterator.OfInt = {
276+
val sp = s.trySplit()
277+
if (sp == null) null else sp.spliterator
278+
}
279+
def estimateSize(): Long = s.estimateSize
280+
def characteristics(): Int = s.characteristics
281+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
282+
override def forEachRemaining(c: IntConsumer): Unit =
283+
while (s.hasStep) { c.accept(s.nextStep()) }
284+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
285+
override def forEachRemaining(c: Consumer[_ >: jl.Integer]): Unit = (c: AnyRef) match {
286+
case ic: IntConsumer => forEachRemaining(ic)
287+
case _ => while (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())) }
288+
}
289+
}
290+
}
291+
292+
/** A Stepper for Doubles. See [[Stepper]]. */
293+
trait DoubleStepper extends Stepper[Double] {
294+
def trySplit(): DoubleStepper
295+
296+
def spliterator[B >: Double]: Spliterator.OfDouble = new DoubleStepper.DoubleStepperSpliterator(this)
297+
298+
def javaIterator[B >: Double]: PrimitiveIterator.OfDouble = new PrimitiveIterator.OfDouble {
299+
def hasNext: Boolean = hasStep
300+
def nextDouble(): Double = nextStep()
301+
}
302+
}
303+
304+
object DoubleStepper {
305+
class DoubleStepperSpliterator(s: DoubleStepper) extends Spliterator.OfDouble {
306+
def tryAdvance(c: DoubleConsumer): Boolean =
307+
if (s.hasStep) { c.accept(s.nextStep()); true } else false
308+
// Override for efficiency: don't wrap the function and call the `tryAdvance` overload
309+
override def tryAdvance(c: Consumer[_ >: jl.Double]): Boolean = (c: AnyRef) match {
310+
case ic: DoubleConsumer => tryAdvance(ic)
311+
case _ => if (s.hasStep) { c.accept(java.lang.Double.valueOf(s.nextStep())); true } else false
312+
}
313+
// override required for dotty#6152
314+
override def trySplit(): Spliterator.OfDouble = {
315+
val sp = s.trySplit()
316+
if (sp == null) null else sp.spliterator
317+
}
318+
def estimateSize(): Long = s.estimateSize
319+
def characteristics(): Int = s.characteristics
320+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
321+
override def forEachRemaining(c: DoubleConsumer): Unit =
322+
while (s.hasStep) { c.accept(s.nextStep()) }
323+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
324+
override def forEachRemaining(c: Consumer[_ >: jl.Double]): Unit = (c: AnyRef) match {
325+
case ic: DoubleConsumer => forEachRemaining(ic)
326+
case _ => while (s.hasStep) { c.accept(jl.Double.valueOf(s.nextStep())) }
327+
}
328+
}
329+
}
330+
331+
/** A Stepper for Longs. See [[Stepper]]. */
332+
trait LongStepper extends Stepper[Long] {
333+
def trySplit(): LongStepper
334+
335+
def spliterator[B >: Long]: Spliterator.OfLong = new LongStepper.LongStepperSpliterator(this)
336+
337+
def javaIterator[B >: Long]: PrimitiveIterator.OfLong = new PrimitiveIterator.OfLong {
338+
def hasNext: Boolean = hasStep
339+
def nextLong(): Long = nextStep()
340+
}
341+
}
342+
343+
object LongStepper {
344+
class LongStepperSpliterator(s: LongStepper) extends Spliterator.OfLong {
345+
def tryAdvance(c: LongConsumer): Boolean =
346+
if (s.hasStep) { c.accept(s.nextStep()); true } else false
347+
// Override for efficiency: don't wrap the function and call the `tryAdvance` overload
348+
override def tryAdvance(c: Consumer[_ >: jl.Long]): Boolean = (c: AnyRef) match {
349+
case ic: LongConsumer => tryAdvance(ic)
350+
case _ => if (s.hasStep) { c.accept(java.lang.Long.valueOf(s.nextStep())); true } else false
351+
}
352+
// override required for dotty#6152
353+
override def trySplit(): Spliterator.OfLong = {
354+
val sp = s.trySplit()
355+
if (sp == null) null else sp.spliterator
356+
}
357+
def estimateSize(): Long = s.estimateSize
358+
def characteristics(): Int = s.characteristics
359+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
360+
override def forEachRemaining(c: LongConsumer): Unit =
361+
while (s.hasStep) { c.accept(s.nextStep()) }
362+
// Override for efficiency: implement with hasStep / nextStep instead of tryAdvance
363+
override def forEachRemaining(c: Consumer[_ >: jl.Long]): Unit = (c: AnyRef) match {
364+
case ic: LongConsumer => forEachRemaining(ic)
365+
case _ => while (s.hasStep) { c.accept(jl.Long.valueOf(s.nextStep())) }
366+
}
367+
}
368+
}

0 commit comments

Comments
 (0)