Skip to content

Commit 4d2cdf3

Browse files
committed
Further constant factor improvements to Catenable
1 parent 796700b commit 4d2cdf3

File tree

1 file changed

+32
-21
lines changed

1 file changed

+32
-21
lines changed

core/shared/src/main/scala/fs2/util/Catenable.scala

+32-21
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,21 @@ sealed abstract class Catenable[+A] {
1313
/** Returns the head and tail of this catenable if non empty, none otherwise. Amortized O(1). */
1414
final def uncons: Option[(A, Catenable[A])] = {
1515
var c: Catenable[A] = this
16-
var rights: List[Catenable[A]] = Nil
16+
val rights = new collection.mutable.ArrayBuffer[Catenable[A]]
1717
var result: Option[(A, Catenable[A])] = null
1818
while (result eq null) {
1919
c match {
2020
case Empty =>
21-
rights match {
22-
case Nil => result = None
23-
case h :: t => c = h; rights = t
21+
if (rights.isEmpty) {
22+
result = None
23+
} else {
24+
c = rights.last
25+
rights.trimEnd(1)
2426
}
2527
case Single(a) =>
26-
val next = if (rights.isEmpty) empty else rights.reverse.reduceLeft((x, y) => Append(y,x))
28+
val next = if (rights.isEmpty) empty else rights.reduceLeft((x, y) => Append(y,x))
2729
result = Some(a -> next)
28-
case Append(l, r) => c = l; rights = r :: rights
30+
case Append(l, r) => c = l; rights += r
2931
}
3032
}
3133
result
@@ -68,19 +70,21 @@ sealed abstract class Catenable[+A] {
6870
/** Applies the supplied function to each element, left to right. */
6971
final def foreach(f: A => Unit): Unit = {
7072
var c: Catenable[A] = this
71-
var rights: List[Catenable[A]] = Nil
73+
val rights = new collection.mutable.ArrayBuffer[Catenable[A]]
7274
while (c ne null) {
7375
c match {
7476
case Empty =>
75-
rights match {
76-
case Nil => c = null
77-
case h :: t => c = h; rights = t
77+
if (rights.isEmpty) {
78+
c = null
79+
} else {
80+
c = rights.last
81+
rights.trimEnd(1)
7882
}
7983
case Single(a) =>
8084
f(a)
81-
c = if (rights.isEmpty) Empty else rights.reverse.reduceLeft((x, y) => Append(y,x))
82-
rights = Nil
83-
case Append(l, r) => c = l; rights = r :: rights
85+
c = if (rights.isEmpty) Empty else rights.reduceLeft((x, y) => Append(y,x))
86+
rights.clear()
87+
case Append(l, r) => c = l; rights += r
8488
}
8589
}
8690
}
@@ -125,14 +129,21 @@ object Catenable {
125129

126130
/** Creates a catenable from the specified elements. */
127131
def apply[A](as: A*): Catenable[A] = {
128-
// Assumption: `as` is small enough that calling size doesn't outweigh benefit of calling empty/single
129-
as.size match {
130-
case 0 => empty
131-
case 1 => single(as.head)
132-
case n if n <= 1024 =>
133-
// nb: avoid cost of reversing input if colection is small
134-
as.view.map(single).reduceRight(Append(_, _))
135-
case n => as.view.reverse.map(single).reduceLeft((x, y) => Append(y, x))
132+
as match {
133+
case w: collection.mutable.WrappedArray[A] =>
134+
if (w.isEmpty) empty
135+
else if (w.size == 1) single(w.head)
136+
else {
137+
val arr: Array[A] = w.array
138+
var c: Catenable[A] = single(arr.last)
139+
var idx = arr.size - 2
140+
while (idx >= 0) {
141+
c = Append(single(arr(idx)), c)
142+
idx -= 1
143+
}
144+
c
145+
}
146+
case _ => fromSeq(as)
136147
}
137148
}
138149
}

0 commit comments

Comments
 (0)