Skip to content

Commit c551fd3

Browse files
committed
Add toIndexedChunk
1 parent 6dcc2c8 commit c551fd3

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

core/shared/src/main/scala/fs2/Chunk.scala

+28-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu
8888
/** Copies the elements of this chunk in to the specified array at the specified start index. */
8989
def copyToArray[O2 >: O](xs: Array[O2], start: Int = 0): Unit
9090

91-
/** Converts this chunk to a chunk backed by a single array. */
91+
/** Converts this chunk to a chunk backed by a single array.
92+
*
93+
* Alternatively, call `toIndexedChunk` to get back a chunk with guaranteed O(1) indexed lookup
94+
* while also minimizing copying.
95+
*/
9296
def compact[O2 >: O](implicit ct: ClassTag[O2]): Chunk.ArraySlice[O2] =
9397
Chunk.ArraySlice(toArray[O2], 0, size)
9498

@@ -309,6 +313,21 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu
309313
if (isEmpty) Chain.empty
310314
else Chain.fromSeq(toList)
311315

316+
/** Returns a chunk with guaranteed O(1) lookup by index.
317+
*
318+
* Unlike `compact`, this operation does not copy any elements unless this chunk
319+
* does not provide O(1) lookup by index -- e.g., a chunk built via 1 or more usages
320+
* of `++`.
321+
*/
322+
def toIndexedChunk: Chunk[O] = this match {
323+
case _: Chunk.Queue[_] =>
324+
val b = collection.mutable.Buffer.newBuilder[O]
325+
b.sizeHint(size)
326+
foreach(o => b += o)
327+
Chunk.buffer(b.result())
328+
case other => other
329+
}
330+
312331
/** Converts this chunk to a list. */
313332
def toList: List[O] =
314333
if (isEmpty) Nil
@@ -1063,6 +1082,14 @@ object Chunk
10631082

10641083
check(chunks, 0)
10651084
}
1085+
1086+
override def traverse[F[_], O2](f: O => F[O2])(implicit F: Applicative[F]): F[Chunk[O2]] =
1087+
toIndexedChunk.traverse(f)
1088+
1089+
override def traverseFilter[F[_], O2](f: O => F[Option[O2]])(implicit
1090+
F: Applicative[F]
1091+
): F[Chunk[O2]] =
1092+
toIndexedChunk.traverseFilter(f)
10661093
}
10671094

10681095
object Queue {

core/shared/src/test/scala/fs2/ChunkSuite.scala

-6
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,4 @@ class ChunkSuite extends Fs2Suite {
215215
test("compactUntagged - regression #2679") {
216216
Chunk.Queue.singleton(Chunk.singleton(1)).traverse(x => Option(x))
217217
}
218-
219-
test("compactBoxed soundness") {
220-
assertEquals(Chunk(1, 2, 3).compactBoxed.compact.values.toList, List(1, 2, 3))
221-
assertEquals(Chunk(1, 2, 3).compactBoxed.toList, List(1, 2, 3))
222-
assertEquals(Chunk(1, 2, 3).compactBoxed.map((_: Int) + 1).toList, List(2, 3, 4))
223-
}
224218
}

0 commit comments

Comments
 (0)