Skip to content

Commit 0230d03

Browse files
committed
Add StrictOptimized ops to stdlib tests
1 parent fa71a56 commit 0230d03

File tree

4 files changed

+401
-0
lines changed

4 files changed

+401
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
collection/StrictOptimizedIterableOps.scala
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
collection/StrictOptimizedSeqOps.scala
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
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
14+
package collection
15+
16+
import scala.annotation.nowarn
17+
import scala.annotation.unchecked.uncheckedVariance
18+
import scala.runtime.Statics
19+
import language.experimental.captureChecking
20+
21+
/**
22+
* Trait that overrides iterable operations to take advantage of strict builders.
23+
*
24+
* @tparam A Elements type
25+
* @tparam CC Collection type constructor
26+
* @tparam C Collection type
27+
*/
28+
trait StrictOptimizedIterableOps[+A, +CC[_], +C]
29+
extends Any
30+
with IterableOps[A, CC, C] {
31+
this: StrictOptimizedIterableOps[A, CC, C] =>
32+
33+
// Optimized, push-based version of `partition`
34+
override def partition(p: A => Boolean): (C, C) = {
35+
val l, r = newSpecificBuilder
36+
iterator.foreach(x => (if (p(x)) l else r) += x)
37+
(l.result(), r.result())
38+
}
39+
40+
override def span(p: A => Boolean): (C, C) = {
41+
val first = newSpecificBuilder
42+
val second = newSpecificBuilder
43+
val it = iterator
44+
var inFirst = true
45+
while (it.hasNext && inFirst) {
46+
val a = it.next()
47+
if (p(a)) {
48+
first += a
49+
} else {
50+
second += a
51+
inFirst = false
52+
}
53+
}
54+
while (it.hasNext) {
55+
second += it.next()
56+
}
57+
(first.result(), second.result())
58+
}
59+
60+
override def unzip[A1, A2](implicit asPair: A -> (A1, A2)): (CC[A1], CC[A2]) = {
61+
val first = iterableFactory.newBuilder[A1]
62+
val second = iterableFactory.newBuilder[A2]
63+
foreach { a =>
64+
val pair = asPair(a)
65+
first += pair._1
66+
second += pair._2
67+
}
68+
(first.result(), second.result())
69+
}
70+
71+
override def unzip3[A1, A2, A3](implicit asTriple: A -> (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = {
72+
val b1 = iterableFactory.newBuilder[A1]
73+
val b2 = iterableFactory.newBuilder[A2]
74+
val b3 = iterableFactory.newBuilder[A3]
75+
76+
foreach { xyz =>
77+
val triple = asTriple(xyz)
78+
b1 += triple._1
79+
b2 += triple._2
80+
b3 += triple._3
81+
}
82+
(b1.result(), b2.result(), b3.result())
83+
}
84+
85+
// The implementations of the following operations are not fundamentally different from
86+
// the view-based implementations, but they turn out to be slightly faster because
87+
// a couple of indirection levels are removed
88+
89+
override def map[B](f: A => B): CC[B] =
90+
strictOptimizedMap(iterableFactory.newBuilder, f)
91+
92+
/**
93+
* @param b Builder to use to build the resulting collection
94+
* @param f Element transformation function
95+
* @tparam B Type of elements of the resulting collection (e.g. `String`)
96+
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
97+
* @return The resulting collection
98+
*/
99+
@inline protected[this] final def strictOptimizedMap[B, C2](b: mutable.Builder[B, C2], f: A => B): C2 = {
100+
val it = iterator
101+
while (it.hasNext) {
102+
b += f(it.next())
103+
}
104+
b.result()
105+
}
106+
107+
override def flatMap[B](f: A => IterableOnce[B]^): CC[B] =
108+
strictOptimizedFlatMap(iterableFactory.newBuilder, f)
109+
110+
/**
111+
* @param b Builder to use to build the resulting collection
112+
* @param f Element transformation function
113+
* @tparam B Type of elements of the resulting collection (e.g. `String`)
114+
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
115+
* @return The resulting collection
116+
*/
117+
@inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2], f: A => IterableOnce[B]^): C2 = {
118+
val it = iterator
119+
while (it.hasNext) {
120+
b ++= f(it.next())
121+
}
122+
b.result()
123+
}
124+
125+
/**
126+
* @param that Elements to concatenate to this collection
127+
* @param b Builder to use to build the resulting collection
128+
* @tparam B Type of elements of the resulting collections (e.g. `Int`)
129+
* @tparam C2 Type of the resulting collection (e.g. `List[Int]`)
130+
* @return The resulting collection
131+
*/
132+
@inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: IterableOnce[B]^, b: mutable.Builder[B, C2]): C2 = {
133+
b ++= this
134+
b ++= that
135+
b.result()
136+
}
137+
138+
override def collect[B](pf: PartialFunction[A, B]^): CC[B] =
139+
strictOptimizedCollect(iterableFactory.newBuilder, pf)
140+
141+
/**
142+
* @param b Builder to use to build the resulting collection
143+
* @param pf Element transformation partial function
144+
* @tparam B Type of elements of the resulting collection (e.g. `String`)
145+
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
146+
* @return The resulting collection
147+
*/
148+
@inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2], pf: PartialFunction[A, B]^): C2 = {
149+
val marker = Statics.pfMarker
150+
val it = iterator
151+
while (it.hasNext) {
152+
val elem = it.next()
153+
val v = pf.applyOrElse(elem, ((x: A) => marker).asInstanceOf[Function[A, B]])
154+
if (marker ne v.asInstanceOf[AnyRef]) b += v
155+
}
156+
b.result()
157+
}
158+
159+
override def flatten[B](implicit toIterableOnce: A -> IterableOnce[B]): CC[B] =
160+
strictOptimizedFlatten(iterableFactory.newBuilder)
161+
162+
/**
163+
* @param b Builder to use to build the resulting collection
164+
* @param toIterableOnce Evidence that `A` can be seen as an `IterableOnce[B]`
165+
* @tparam B Type of elements of the resulting collection (e.g. `Int`)
166+
* @tparam C2 Type of the resulting collection (e.g. `List[Int]`)
167+
* @return The resulting collection
168+
*/
169+
@inline protected[this] final def strictOptimizedFlatten[B, C2](b: mutable.Builder[B, C2])(implicit toIterableOnce: A -> IterableOnce[B]): C2 = {
170+
val it = iterator
171+
while (it.hasNext) {
172+
b ++= toIterableOnce(it.next())
173+
}
174+
b.result()
175+
}
176+
177+
override def zip[B](that: IterableOnce[B]^): CC[(A @uncheckedVariance, B)] =
178+
strictOptimizedZip(that, iterableFactory.newBuilder[(A, B)])
179+
180+
/**
181+
* @param that Collection to zip with this collection
182+
* @param b Builder to use to build the resulting collection
183+
* @tparam B Type of elements of the second collection (e.g. `String`)
184+
* @tparam C2 Type of the resulting collection (e.g. `List[(Int, String)]`)
185+
* @return The resulting collection
186+
*/
187+
@inline protected[this] final def strictOptimizedZip[B, C2](that: IterableOnce[B]^, b: mutable.Builder[(A, B), C2]): C2 = {
188+
val it1 = iterator
189+
val it2 = that.iterator
190+
while (it1.hasNext && it2.hasNext) {
191+
b += ((it1.next(), it2.next()))
192+
}
193+
b.result()
194+
}
195+
196+
override def zipWithIndex: CC[(A @uncheckedVariance, Int)] = {
197+
val b = iterableFactory.newBuilder[(A, Int)]
198+
var i = 0
199+
val it = iterator
200+
while (it.hasNext) {
201+
b += ((it.next(), i))
202+
i += 1
203+
}
204+
b.result()
205+
}
206+
207+
override def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = {
208+
val b = iterableFactory.newBuilder[B]
209+
b.sizeHint(this, delta = 0)
210+
var acc = z
211+
b += acc
212+
val it = iterator
213+
while (it.hasNext) {
214+
acc = op(acc, it.next())
215+
b += acc
216+
}
217+
b.result()
218+
}
219+
220+
override def filter(pred: A => Boolean): C = filterImpl(pred, isFlipped = false)
221+
222+
override def filterNot(pred: A => Boolean): C = filterImpl(pred, isFlipped = true)
223+
224+
protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): C = {
225+
val b = newSpecificBuilder
226+
val it = iterator
227+
while (it.hasNext) {
228+
val elem = it.next()
229+
if (pred(elem) != isFlipped) {
230+
b += elem
231+
}
232+
}
233+
b.result()
234+
}
235+
236+
// Optimized, push-based version of `partitionMap`
237+
override def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1], CC[A2]) = {
238+
val l = iterableFactory.newBuilder[A1]
239+
val r = iterableFactory.newBuilder[A2]
240+
foreach { x =>
241+
f(x) match {
242+
case Left(x1) => l += x1
243+
case Right(x2) => r += x2
244+
}
245+
}
246+
(l.result(), r.result())
247+
}
248+
249+
// Optimization avoids creation of second collection
250+
override def tapEach[U](f: A => U): C = {
251+
foreach(f)
252+
coll
253+
}
254+
255+
/** A collection containing the last `n` elements of this collection.
256+
* $willForceEvaluation
257+
*/
258+
override def takeRight(n: Int): C = {
259+
val b = newSpecificBuilder
260+
b.sizeHintBounded(n, toIterable: @nowarn("cat=deprecation"))
261+
val lead = iterator drop n
262+
val it = iterator
263+
while (lead.hasNext) {
264+
lead.next()
265+
it.next()
266+
}
267+
while (it.hasNext) b += it.next()
268+
b.result()
269+
}
270+
271+
/** The rest of the collection without its `n` last elements. For
272+
* linear, immutable collections this should avoid making a copy.
273+
* $willForceEvaluation
274+
*/
275+
override def dropRight(n: Int): C = {
276+
val b = newSpecificBuilder
277+
if (n >= 0) b.sizeHint(this, delta = -n)
278+
val lead = iterator drop n
279+
val it = iterator
280+
while (lead.hasNext) {
281+
b += it.next()
282+
lead.next()
283+
}
284+
b.result()
285+
}
286+
}

0 commit comments

Comments
 (0)