Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,67 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
ByteString.empty.copyToBuffer(ByteBuffer.allocate(10)) should ===(0)
}

"copyToBuffer(buffer, offset) copies from the given offset for different ByteString types" in {
import java.nio.ByteBuffer

// ByteString1C — copy from offset 2
val bs1c = ByteString1C(Array[Byte](1, 2, 3, 4, 5))
val buf1 = ByteBuffer.allocate(5)
bs1c.copyToBuffer(buf1, 2) should ===(3)
buf1.flip()
val result1 = new Array[Byte](3)
buf1.get(result1)
result1.toSeq should ===(Seq[Byte](3, 4, 5))

// ByteString1C — offset beyond length copies nothing
val buf2 = ByteBuffer.allocate(5)
bs1c.copyToBuffer(buf2, 10) should ===(0)

// ByteString1C — offset 0 copies all
val buf3 = ByteBuffer.allocate(5)
bs1c.copyToBuffer(buf3, 0) should ===(5)

// ByteString1 — copy from offset 1 (internal startIndex + user offset)
val bs1 = ByteString1(Array[Byte](0, 10, 20, 30, 40, 50), 1, 4) // logical bytes [10, 20, 30, 40]
val buf4 = ByteBuffer.allocate(4)
bs1.copyToBuffer(buf4, 1) should ===(3)
buf4.flip()
val result4 = new Array[Byte](3)
buf4.get(result4)
result4.toSeq should ===(Seq[Byte](20, 30, 40))

// ByteString1 — offset 0 copies all logical bytes
val buf5 = ByteBuffer.allocate(4)
bs1.copyToBuffer(buf5, 0) should ===(4)
buf5.flip()
buf5.get() should ===(10.toByte)

// ByteStrings — copy from offset that skips first segment entirely
val bss = ByteStrings(ByteString1.fromString("abc"), ByteString1.fromString("def"))
val buf6 = ByteBuffer.allocate(10)
bss.copyToBuffer(buf6, 3) should ===(3)
buf6.flip()
val result6 = new Array[Byte](3)
buf6.get(result6)
result6.toSeq should ===(Seq[Byte]('d', 'e', 'f'))

// ByteStrings — copy from offset mid-first-segment
val buf7 = ByteBuffer.allocate(10)
bss.copyToBuffer(buf7, 1) should ===(5)
buf7.flip()
val result7 = new Array[Byte](5)
buf7.get(result7)
result7.toSeq should ===(Seq[Byte]('b', 'c', 'd', 'e', 'f'))

// ByteStrings — offset 0 copies all
val buf8 = ByteBuffer.allocate(6)
bss.copyToBuffer(buf8, 0) should ===(6)

// ByteString.empty — any offset copies nothing
ByteString.empty.copyToBuffer(ByteBuffer.allocate(10), 0) should ===(0)
ByteString.empty.copyToBuffer(ByteBuffer.allocate(10), 5) should ===(0)
}

"copying chunks to an array" in {
val iterator = (ByteString("123") ++ ByteString("456")).iterator
val array = Array.ofDim[Byte](6)
Expand Down
21 changes: 13 additions & 8 deletions actor/src/main/scala/org/apache/pekko/util/ByteString.scala
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,7 @@ object ByteString {
override def copyToBuffer(buffer: ByteBuffer): Int =
writeToBuffer(buffer, offset = 0)

/** INTERNAL API: Specialized for internal use, copying from an offset without slicing. */
private[pekko] override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
writeToBuffer(buffer, offset)

/** INTERNAL API: Specialized for internal use, writing multiple ByteString1C into the same ByteBuffer. */
Expand Down Expand Up @@ -576,8 +575,7 @@ object ByteString {
override def copyToBuffer(buffer: ByteBuffer): Int =
writeToBuffer(buffer, offset = 0)

/** INTERNAL API: Specialized for internal use, copying from an offset without slicing. */
private[pekko] override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
writeToBuffer(buffer, offset)

/** INTERNAL API: Specialized for internal use, writing multiple ByteString1C into the same ByteBuffer. */
Expand Down Expand Up @@ -993,8 +991,7 @@ object ByteString {
override def copyToBuffer(buffer: ByteBuffer): Int =
copyToBuffer(buffer, offset = 0)

/** INTERNAL API: Specialized for internal use, copying from an offset without slicing. */
private[pekko] override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int = {
override def copyToBuffer(buffer: ByteBuffer, offset: Int): Int = {
var remainingOffset = offset
var written = 0
var i = 0
Expand Down Expand Up @@ -1899,8 +1896,16 @@ sealed abstract class ByteString
*/
def copyToBuffer(@nowarn("msg=never used") buffer: ByteBuffer): Int

/** INTERNAL API: Copy bytes to a ByteBuffer from a ByteString offset without allocating a slice. */
private[pekko] def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
/**
* Copy as many bytes as possible to a ByteBuffer, starting from a given offset within this ByteString
* and the buffer's current position. This method will not overflow the buffer.
*
* @param buffer a ByteBuffer to copy bytes to
* @param offset the offset within this ByteString to start copying from
* @return the number of bytes actually copied
* @since 2.0.0
*/
def copyToBuffer(buffer: ByteBuffer, offset: Int): Int =
if (offset <= 0) copyToBuffer(buffer)
else drop(offset).copyToBuffer(buffer)

Expand Down