Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialize every Chunk-to-ByteVector conversion #2956

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions core/shared/src/main/scala/fs2/Chunk.scala
Original file line number Diff line number Diff line change
Expand Up @@ -362,19 +362,7 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu
}

/** Converts this chunk to a scodec-bits ByteVector. */
def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector =
this match {
case c: Chunk.ByteVectorChunk => c.toByteVector
case c: Chunk.ByteBuffer =>
// if c is a view, position/limit of underlying buffer may have changed
val bb = c.buf.asReadOnlyBuffer()
bb.position(c.offset)
bb.limit(c.offset + c.size)
ByteVector.view(bb)
case other =>
val slice = other.asInstanceOf[Chunk[Byte]].toArraySlice
ByteVector.view(slice.values, slice.offset, slice.length)
}
def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector

/** Converts this chunk to a scodec-bits BitVector. */
def toBitVector[B >: O](implicit ev: B =:= Byte): BitVector = toByteVector[B].bits
Expand Down Expand Up @@ -533,6 +521,7 @@ object Chunk
protected def splitAtChunk_(n: Int): (Chunk[Nothing], Chunk[Nothing]) =
sys.error("impossible")
override def map[O2](f: Nothing => O2): Chunk[O2] = this
override def toByteVector[B](implicit ev: B =:= Byte): ByteVector = ByteVector.empty
override def toString = "empty"
}

Expand All @@ -549,6 +538,8 @@ object Chunk
def apply(i: Int): O =
if (i == 0) value else throw new IndexOutOfBoundsException()
def copyToArray[O2 >: O](xs: Array[O2], start: Int): Unit = xs(start) = value
override def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector =
ByteVector.fromByte(value)
protected def splitAtChunk_(n: Int): (Chunk[O], Chunk[O]) =
sys.error("impossible")
override def map[O2](f: O => O2): Chunk[O2] = singleton(f(value))
Expand All @@ -571,6 +562,10 @@ object Chunk
s.copyToArray(xs, start)
()
}

override def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector =
ByteVector.viewAt(idx => s(idx.toInt), s.length.toLong)

override def toVector = s.toVector

override def drop(n: Int): Chunk[O] =
Expand Down Expand Up @@ -704,6 +699,11 @@ object Chunk
()
}

override def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector =
if (values.isInstanceOf[Array[Byte]])
ByteVector.view(values.asInstanceOf[Array[Byte]], offset, length)
else ByteVector.viewAt(i => apply(i.toInt), size.toLong)

protected def splitAtChunk_(n: Int): (Chunk[O], Chunk[O]) =
ArraySlice(values, offset, n) -> ArraySlice(values, offset + n, length - n)

Expand Down Expand Up @@ -802,6 +802,9 @@ object Chunk
b.get(dest, offset, length)

def duplicate(b: JCharBuffer): JCharBuffer = b.duplicate()

override def toByteVector[B >: Char](implicit ev: B =:= Byte): ByteVector =
throw new UnsupportedOperationException
}

/** Creates a chunk backed by an char buffer, bounded by the current position and limit */
Expand Down Expand Up @@ -832,6 +835,13 @@ object Chunk
b.get(dest, offset, length)

def duplicate(b: JByteBuffer): JByteBuffer = b.duplicate()

override def toByteVector[B >: Byte](implicit ev: B =:= Byte): ByteVector = {
val bb = buf.asReadOnlyBuffer()
bb.position(offset)
bb.limit(offset + size)
ByteVector.view(bb)
}
}

/** Creates a chunk backed by an byte buffer, bounded by the current position and limit */
Expand All @@ -841,19 +851,19 @@ object Chunk
def byteVector(bv: ByteVector): Chunk[Byte] =
ByteVectorChunk(bv)

private case class ByteVectorChunk(toByteVector: ByteVector) extends Chunk[Byte] {
private case class ByteVectorChunk(bv: ByteVector) extends Chunk[Byte] {

def apply(i: Int): Byte =
toByteVector(i.toLong)
bv(i.toLong)

def size: Int =
toByteVector.size.toInt
bv.size.toInt

def copyToArray[O2 >: Byte](xs: Array[O2], start: Int): Unit =
if (xs.isInstanceOf[Array[Byte]])
toByteVector.copyToArray(xs.asInstanceOf[Array[Byte]], start)
bv.copyToArray(xs.asInstanceOf[Array[Byte]], start)
else {
toByteVector.toIndexedSeq.copyToArray(xs, start)
bv.toIndexedSeq.copyToArray(xs, start)
()
}

Expand All @@ -874,6 +884,8 @@ object Chunk

override def map[O2](f: Byte => O2): Chunk[O2] =
Chunk.indexedSeq(toByteVector.toIndexedSeq.map(f))

override def toByteVector[B >: Byte](implicit ev: B =:= Byte): ByteVector = bv
}

/** Concatenates the specified sequence of chunks in to a single chunk, avoiding boxing. */
Expand Down Expand Up @@ -1008,6 +1020,9 @@ object Chunk
go(chunks, start)
}

override def toByteVector[B >: O](implicit ev: B =:= Byte): ByteVector =
chunks.foldLeft(ByteVector.empty)(_ ++ _.toByteVector(ev))

override def take(n: Int): Queue[O] =
if (n <= 0) Queue.empty
else if (n >= size) this
Expand Down
4 changes: 4 additions & 0 deletions core/shared/src/test/scala/fs2/ChunkSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ class ChunkSuite extends Fs2Suite {
Chunk.ArraySlice(Array[Any](0)).asInstanceOf[Chunk[Int]].toArray[Int]
}

test("ArraySlice toByteVector") {
Chunk.ArraySlice(Array[Any](0.toByte)).asInstanceOf[Chunk[Byte]].toByteVector
}

test("ArraySlice does not copy when chunk is already an ArraySlice instance") {
val chunk: Chunk[Int] = Chunk.ArraySlice(Array(0))
assert(chunk eq chunk.toArraySlice)
Expand Down