This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git
The following commit(s) were added to refs/heads/main by this push:
new bc78cd16d5 ByteStringBuilder.putInt/putLong: eliminate closure
allocation via SWARUtil VarHandle writes (#2879)
bc78cd16d5 is described below
commit bc78cd16d554e3c3ce6c8fd3345a390ce06049fb
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Apr 24 00:18:55 2026 +0200
ByteStringBuilder.putInt/putLong: eliminate closure allocation via SWARUtil
VarHandle writes (#2879)
* Eliminate closure allocation in ByteStringBuilder.putInt/putLong via
SWARUtil VarHandle writes
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/c0a324ab-b053-4254-8d3b-e472278559b9
Co-authored-by: pjfanning <[email protected]>
* Add tests for SWARUtil.putInt/putLong write methods and
ByteStringBuilder.putInt/putLong
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/d3bbcf71-0623-473b-b9cc-52a23c034666
Co-authored-by: pjfanning <[email protected]>
* scalafmt
* Create ByteString_putInt_Benchmark.scala
* Add putShort VarHandle support, fix benchmark, add tests for putShort
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/710c7bd8-24f1-4665-b722-dd9020049450
Co-authored-by: pjfanning <[email protected]>
* Refactor SnapshotSerializer: remove writeInt, use SWARUtil.putInt +
pre-allocated arrays
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/770d8c57-8a12-4f06-b900-1f6271577fdd
Co-authored-by: pjfanning <[email protected]>
* refactor to avoid one array creation on one path
* plan: fix putShort Int param, add all-zero/all-ones layout tests,
putFloat/putDouble tests, SnapshotSerializer format tests
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/3a10e6a7-4e61-4f22-9ca1-fdbad5891385
Co-authored-by: pjfanning <[email protected]>
* Fix putShort to accept Int; add all-zero/all-ones layout tests; add
putFloat/putDouble and SnapshotSerializer format tests
Agent-Logs-Url:
https://github.com/pjfanning/incubator-pekko/sessions/3a10e6a7-4e61-4f22-9ca1-fdbad5891385
Co-authored-by: pjfanning <[email protected]>
---------
Co-authored-by: copilot-swe-agent[bot]
<[email protected]>
Co-authored-by: pjfanning <[email protected]>
---
.../org/apache/pekko/util/ByteStringSpec.scala | 373 +++++++++++++++++++++
.../scala/org/apache/pekko/util/SWARUtilSpec.scala | 222 ++++++++++++
.../scala/org/apache/pekko/util/ByteString.scala | 54 +--
.../scala/org/apache/pekko/util/SWARUtil.scala | 124 +++++++
.../pekko/util/ByteString_putInt_Benchmark.scala | 101 ++++++
.../serialization/SnapshotSerializer.scala | 38 +--
6 files changed, 851 insertions(+), 61 deletions(-)
diff --git
a/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
b/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
index fcfc4fcca7..1956c556a4 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
@@ -2693,6 +2693,379 @@ class ByteStringSpec extends AnyWordSpec with Matchers
with Checkers {
}
}
}
+
+ "putInt" when {
+ "big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(0x01020304)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x01, 0x02, 0x03, 0x04))
+ }
+ "little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(0x01020304)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x04, 0x03, 0x02, 0x01))
+ }
+ "max value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(Int.MaxValue)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x7F, 0xFF.toByte,
0xFF.toByte, 0xFF.toByte))
+ }
+ "min value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(Int.MinValue)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x80.toByte, 0x00, 0x00,
0x00))
+ }
+ "max value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(Int.MaxValue)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0xFF.toByte, 0xFF.toByte,
0xFF.toByte, 0x7F))
+ }
+ "min value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(Int.MinValue)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x00, 0x00, 0x00,
0x80.toByte))
+ }
+ "result has length 4" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(42)(BIG_ENDIAN)
+ builder.length should ===(4)
+ builder.result().length should ===(4)
+ }
+ "multiple puts accumulate correctly" in {
+ val builder = ByteString.newBuilder
+ builder.putInt(0x01020304)(BIG_ENDIAN)
+ builder.putInt(0x05060708)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08))
+ }
+ "round-trips with ByteBuffer big-endian" in {
+ check { (value: Int) =>
+ val builder = ByteString.newBuilder
+ builder.putInt(value)(BIG_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putInt(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ "round-trips with ByteBuffer little-endian" in {
+ check { (value: Int) =>
+ val builder = ByteString.newBuilder
+ builder.putInt(value)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putInt(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ }
+
+ "putLong" when {
+ "big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(0x0102030405060708L)(BIG_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08))
+ }
+ "little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(0x0102030405060708L)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01))
+ }
+ "max value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(Long.MaxValue)(BIG_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x7F, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte,
0xFF.toByte, 0xFF.toByte, 0xFF.toByte))
+ }
+ "min value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(Long.MinValue)(BIG_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x80.toByte, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))
+ }
+ "max value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(Long.MaxValue)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte,
0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x7F))
+ }
+ "min value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(Long.MinValue)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80.toByte))
+ }
+ "result has length 8" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(42L)(BIG_ENDIAN)
+ builder.length should ===(8)
+ builder.result().length should ===(8)
+ }
+ "multiple puts accumulate correctly" in {
+ val builder = ByteString.newBuilder
+ builder.putLong(0x0102030405060708L)(BIG_ENDIAN)
+ builder.putLong(0x090A0B0C0D0E0F10L)(BIG_ENDIAN)
+ builder.result().toSeq should ===(
+ Seq[Byte](0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10))
+ }
+ "round-trips with ByteBuffer big-endian" in {
+ check { (value: Long) =>
+ val builder = ByteString.newBuilder
+ builder.putLong(value)(BIG_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putLong(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ "round-trips with ByteBuffer little-endian" in {
+ check { (value: Long) =>
+ val builder = ByteString.newBuilder
+ builder.putLong(value)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putLong(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ }
+
+ "putFloat" when {
+ "big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(1.0f)(BIG_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putFloat(1.0f)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(1.0f)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putFloat(1.0f)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "max value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(Float.MaxValue)(BIG_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putFloat(Float.MaxValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "min value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(Float.MinValue)(BIG_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putFloat(Float.MinValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "max value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(Float.MaxValue)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](4)
+
ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putFloat(Float.MaxValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "min value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(Float.MinValue)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](4)
+
ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putFloat(Float.MinValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "NaN big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(Float.NaN)(BIG_ENDIAN)
+ val bits = floatToRawIntBits(Float.NaN)
+ builder.result().toSeq should ===(
+ Seq[Byte]((bits >>> 24).toByte, (bits >>> 16).toByte, (bits >>>
8).toByte, bits.toByte))
+ }
+ "result has length 4" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(42.0f)(BIG_ENDIAN)
+ builder.length should ===(4)
+ builder.result().length should ===(4)
+ }
+ "multiple puts accumulate correctly" in {
+ val builder = ByteString.newBuilder
+ builder.putFloat(1.0f)(BIG_ENDIAN)
+ builder.putFloat(2.0f)(BIG_ENDIAN)
+ builder.length should ===(8)
+ val reference = new Array[Byte](8)
+ val buf = ByteBuffer.wrap(reference).order(BIG_ENDIAN)
+ buf.putFloat(1.0f)
+ buf.putFloat(2.0f)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "round-trips with ByteBuffer big-endian" in {
+ check { (value: Float) =>
+ val builder = ByteString.newBuilder
+ builder.putFloat(value)(BIG_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putFloat(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ "round-trips with ByteBuffer little-endian" in {
+ check { (value: Float) =>
+ val builder = ByteString.newBuilder
+ builder.putFloat(value)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](4)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putFloat(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ }
+
+ "putDouble" when {
+ "big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(1.0)(BIG_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putDouble(1.0)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(1.0)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putDouble(1.0)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "max value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(Double.MaxValue)(BIG_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putDouble(Double.MaxValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "min value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(Double.MinValue)(BIG_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putDouble(Double.MinValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "max value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(Double.MaxValue)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](8)
+
ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putDouble(Double.MaxValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "min value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(Double.MinValue)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](8)
+
ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putDouble(Double.MinValue)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "NaN big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(Double.NaN)(BIG_ENDIAN)
+ val bits = doubleToRawLongBits(Double.NaN)
+ builder.result().toSeq should ===(
+ Seq[Byte](
+ (bits >>> 56).toByte, (bits >>> 48).toByte, (bits >>> 40).toByte,
(bits >>> 32).toByte,
+ (bits >>> 24).toByte, (bits >>> 16).toByte, (bits >>> 8).toByte,
bits.toByte))
+ }
+ "result has length 8" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(42.0)(BIG_ENDIAN)
+ builder.length should ===(8)
+ builder.result().length should ===(8)
+ }
+ "multiple puts accumulate correctly" in {
+ val builder = ByteString.newBuilder
+ builder.putDouble(1.0)(BIG_ENDIAN)
+ builder.putDouble(2.0)(BIG_ENDIAN)
+ builder.length should ===(16)
+ val reference = new Array[Byte](16)
+ val buf = ByteBuffer.wrap(reference).order(BIG_ENDIAN)
+ buf.putDouble(1.0)
+ buf.putDouble(2.0)
+ builder.result().toSeq should ===(reference.toSeq)
+ }
+ "round-trips with ByteBuffer big-endian" in {
+ check { (value: Double) =>
+ val builder = ByteString.newBuilder
+ builder.putDouble(value)(BIG_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putDouble(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ "round-trips with ByteBuffer little-endian" in {
+ check { (value: Double) =>
+ val builder = ByteString.newBuilder
+ builder.putDouble(value)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](8)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putDouble(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ }
+
+ "putShort" when {
+ "big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(0x0102)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x01, 0x02))
+ }
+ "little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(0x0102)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x02, 0x01))
+ }
+ "max value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(Short.MaxValue.toInt)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x7F, 0xFF.toByte))
+ }
+ "min value big-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(Short.MinValue.toInt)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x80.toByte, 0x00))
+ }
+ "max value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(Short.MaxValue.toInt)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0xFF.toByte, 0x7F))
+ }
+ "min value little-endian" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(Short.MinValue.toInt)(LITTLE_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x00, 0x80.toByte))
+ }
+ "result has length 2" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(42)(BIG_ENDIAN)
+ builder.length should ===(2)
+ builder.result().length should ===(2)
+ }
+ "multiple puts accumulate correctly" in {
+ val builder = ByteString.newBuilder
+ builder.putShort(0x0102)(BIG_ENDIAN)
+ builder.putShort(0x0304)(BIG_ENDIAN)
+ builder.result().toSeq should ===(Seq[Byte](0x01, 0x02, 0x03, 0x04))
+ }
+ "round-trips with ByteBuffer big-endian" in {
+ check { (value: Short) =>
+ val builder = ByteString.newBuilder
+ builder.putShort(value.toInt)(BIG_ENDIAN)
+ val reference = new Array[Byte](2)
+ ByteBuffer.wrap(reference).order(BIG_ENDIAN).putShort(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ "round-trips with ByteBuffer little-endian" in {
+ check { (value: Short) =>
+ val builder = ByteString.newBuilder
+ builder.putShort(value.toInt)(LITTLE_ENDIAN)
+ val reference = new Array[Byte](2)
+ ByteBuffer.wrap(reference).order(LITTLE_ENDIAN).putShort(value)
+ builder.result().toSeq == reference.toSeq
+ }
+ }
+ }
}
private def makeMultiByteStringsSample(): ByteString = {
diff --git
a/actor-tests/src/test/scala/org/apache/pekko/util/SWARUtilSpec.scala
b/actor-tests/src/test/scala/org/apache/pekko/util/SWARUtilSpec.scala
index 9a9688ded6..a88d6b5e8f 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/util/SWARUtilSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/util/SWARUtilSpec.scala
@@ -57,6 +57,228 @@ class SWARUtilSpec extends AnyWordSpec with Matchers {
SWARUtil.getShortBEWithoutMethodHandle(testData, 2) should
===(0x0203.toShort)
SWARUtil.getShortLEWithoutMethodHandle(testData, 2) should
===(0x0302.toShort)
}
+ "putInt" in {
+ val arr = new Array[Byte](8)
+
+ SWARUtil.putInt(arr, 0, 0x00010203, ByteOrder.BIG_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0x00, 0x01, 0x02, 0x03))
+
+ SWARUtil.putInt(arr, 4, 0x04050607, ByteOrder.BIG_ENDIAN)
+ arr.drop(4).toSeq should ===(Seq[Byte](0x04, 0x05, 0x06, 0x07))
+
+ SWARUtil.putInt(arr, 0, 0x00010203, ByteOrder.LITTLE_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0x03, 0x02, 0x01, 0x00))
+
+ SWARUtil.putInt(arr, 4, 0x04050607, ByteOrder.LITTLE_ENDIAN)
+ arr.drop(4).toSeq should ===(Seq[Byte](0x07, 0x06, 0x05, 0x04))
+
+ // all-zero pattern
+ SWARUtil.putInt(arr, 0, 0, ByteOrder.BIG_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0x00, 0x00, 0x00, 0x00))
+
+ SWARUtil.putInt(arr, 0, 0, ByteOrder.LITTLE_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0x00, 0x00, 0x00, 0x00))
+
+ // all-ones pattern
+ SWARUtil.putInt(arr, 0, -1, ByteOrder.BIG_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0xFF.toByte, 0xFF.toByte,
0xFF.toByte, 0xFF.toByte))
+
+ SWARUtil.putInt(arr, 0, -1, ByteOrder.LITTLE_ENDIAN)
+ arr.take(4).toSeq should ===(Seq[Byte](0xFF.toByte, 0xFF.toByte,
0xFF.toByte, 0xFF.toByte))
+ }
+ "putIntBEWithoutMethodHandle" in {
+ val arr = new Array[Byte](8)
+
+ SWARUtil.putIntBEWithoutMethodHandle(arr, 0, 0x00010203)
+ arr.take(4).toSeq should ===(Seq[Byte](0x00, 0x01, 0x02, 0x03))
+
+ SWARUtil.putIntBEWithoutMethodHandle(arr, 4, 0x04050607)
+ arr.drop(4).toSeq should ===(Seq[Byte](0x04, 0x05, 0x06, 0x07))
+
+ // round-trip: putIntBEWithoutMethodHandle and
getIntBEWithoutMethodHandle are inverses
+ SWARUtil.putIntBEWithoutMethodHandle(arr, 0, Int.MaxValue)
+ SWARUtil.getIntBEWithoutMethodHandle(arr, 0) should ===(Int.MaxValue)
+ SWARUtil.putIntBEWithoutMethodHandle(arr, 0, Int.MinValue)
+ SWARUtil.getIntBEWithoutMethodHandle(arr, 0) should ===(Int.MinValue)
+ }
+ "putIntLEWithoutMethodHandle" in {
+ val arr = new Array[Byte](8)
+
+ SWARUtil.putIntLEWithoutMethodHandle(arr, 0, 0x00010203)
+ arr.take(4).toSeq should ===(Seq[Byte](0x03, 0x02, 0x01, 0x00))
+
+ SWARUtil.putIntLEWithoutMethodHandle(arr, 4, 0x04050607)
+ arr.drop(4).toSeq should ===(Seq[Byte](0x07, 0x06, 0x05, 0x04))
+
+ // round-trip
+ SWARUtil.putIntLEWithoutMethodHandle(arr, 0, Int.MaxValue)
+ SWARUtil.getIntLEWithoutMethodHandle(arr, 0) should ===(Int.MaxValue)
+ SWARUtil.putIntLEWithoutMethodHandle(arr, 0, Int.MinValue)
+ SWARUtil.getIntLEWithoutMethodHandle(arr, 0) should ===(Int.MinValue)
+ }
+ "putLong" in {
+ val arr = new Array[Byte](16)
+
+ SWARUtil.putLong(arr, 0, 0x0001020304050607L, ByteOrder.BIG_ENDIAN)
+ arr.take(8).toSeq should ===(Seq[Byte](0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07))
+
+ SWARUtil.putLong(arr, 8, 0x08090A0B0C0D0E0FL, ByteOrder.BIG_ENDIAN)
+ arr.drop(8).toSeq should ===(Seq[Byte](0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F))
+
+ SWARUtil.putLong(arr, 0, 0x0001020304050607L, ByteOrder.LITTLE_ENDIAN)
+ arr.take(8).toSeq should ===(Seq[Byte](0x07, 0x06, 0x05, 0x04, 0x03,
0x02, 0x01, 0x00))
+
+ SWARUtil.putLong(arr, 8, 0x08090A0B0C0D0E0FL, ByteOrder.LITTLE_ENDIAN)
+ arr.drop(8).toSeq should ===(Seq[Byte](0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
0x0A, 0x09, 0x08))
+
+ // all-zero pattern
+ SWARUtil.putLong(arr, 0, 0L, ByteOrder.BIG_ENDIAN)
+ arr.take(8).toSeq should ===(Seq.fill(8)(0x00.toByte))
+
+ SWARUtil.putLong(arr, 0, 0L, ByteOrder.LITTLE_ENDIAN)
+ arr.take(8).toSeq should ===(Seq.fill(8)(0x00.toByte))
+
+ // all-ones pattern
+ SWARUtil.putLong(arr, 0, -1L, ByteOrder.BIG_ENDIAN)
+ arr.take(8).toSeq should ===(Seq.fill(8)(0xFF.toByte))
+
+ SWARUtil.putLong(arr, 0, -1L, ByteOrder.LITTLE_ENDIAN)
+ arr.take(8).toSeq should ===(Seq.fill(8)(0xFF.toByte))
+ }
+ "putLongBEWithoutMethodHandle" in {
+ val arr = new Array[Byte](16)
+
+ SWARUtil.putLongBEWithoutMethodHandle(arr, 0, 0x0001020304050607L)
+ arr.take(8).toSeq should ===(Seq[Byte](0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07))
+
+ SWARUtil.putLongBEWithoutMethodHandle(arr, 8, 0x08090A0B0C0D0E0FL)
+ arr.drop(8).toSeq should ===(Seq[Byte](0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F))
+
+ // round-trip
+ SWARUtil.putLongBEWithoutMethodHandle(arr, 0, Long.MaxValue)
+ SWARUtil.getLongBEWithoutMethodHandle(arr, 0) should ===(Long.MaxValue)
+ SWARUtil.putLongBEWithoutMethodHandle(arr, 0, Long.MinValue)
+ SWARUtil.getLongBEWithoutMethodHandle(arr, 0) should ===(Long.MinValue)
+ }
+ "putLongLEWithoutMethodHandle" in {
+ val arr = new Array[Byte](16)
+
+ SWARUtil.putLongLEWithoutMethodHandle(arr, 0, 0x0001020304050607L)
+ arr.take(8).toSeq should ===(Seq[Byte](0x07, 0x06, 0x05, 0x04, 0x03,
0x02, 0x01, 0x00))
+
+ SWARUtil.putLongLEWithoutMethodHandle(arr, 8, 0x08090A0B0C0D0E0FL)
+ arr.drop(8).toSeq should ===(Seq[Byte](0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
0x0A, 0x09, 0x08))
+
+ // round-trip
+ SWARUtil.putLongLEWithoutMethodHandle(arr, 0, Long.MaxValue)
+ SWARUtil.getLongLEWithoutMethodHandle(arr, 0) should ===(Long.MaxValue)
+ SWARUtil.putLongLEWithoutMethodHandle(arr, 0, Long.MinValue)
+ SWARUtil.getLongLEWithoutMethodHandle(arr, 0) should ===(Long.MinValue)
+ }
+ "putInt and getInt are inverses" in {
+ val arr = new Array[Byte](4)
+ for (value <- Seq(0, 1, -1, Int.MaxValue, Int.MinValue, 0x12345678)) {
+ SWARUtil.putInt(arr, 0, value, ByteOrder.BIG_ENDIAN)
+ SWARUtil.getInt(arr, 0, ByteOrder.BIG_ENDIAN) should ===(value)
+ SWARUtil.putInt(arr, 0, value, ByteOrder.LITTLE_ENDIAN)
+ SWARUtil.getInt(arr, 0, ByteOrder.LITTLE_ENDIAN) should ===(value)
+ }
+ }
+ "putLong and getLong are inverses" in {
+ val arr = new Array[Byte](8)
+ for (value <- Seq(0L, 1L, -1L, Long.MaxValue, Long.MinValue,
0x123456789ABCDEF0L)) {
+ SWARUtil.putLong(arr, 0, value, ByteOrder.BIG_ENDIAN)
+ SWARUtil.getLong(arr, 0, ByteOrder.BIG_ENDIAN) should ===(value)
+ SWARUtil.putLong(arr, 0, value, ByteOrder.LITTLE_ENDIAN)
+ SWARUtil.getLong(arr, 0, ByteOrder.LITTLE_ENDIAN) should ===(value)
+ }
+ }
+ "putInt writes at correct offset" in {
+ val arr = new Array[Byte](6)
+ SWARUtil.putInt(arr, 1, 0x01020304, ByteOrder.BIG_ENDIAN)
+ arr(0) should ===(0.toByte)
+ arr(1) should ===(0x01.toByte)
+ arr(2) should ===(0x02.toByte)
+ arr(3) should ===(0x03.toByte)
+ arr(4) should ===(0x04.toByte)
+ arr(5) should ===(0.toByte)
+ }
+ "putLong writes at correct offset" in {
+ val arr = new Array[Byte](10)
+ SWARUtil.putLong(arr, 1, 0x0102030405060708L, ByteOrder.BIG_ENDIAN)
+ arr(0) should ===(0.toByte)
+ arr(1) should ===(0x01.toByte)
+ arr(2) should ===(0x02.toByte)
+ arr(3) should ===(0x03.toByte)
+ arr(4) should ===(0x04.toByte)
+ arr(5) should ===(0x05.toByte)
+ arr(6) should ===(0x06.toByte)
+ arr(7) should ===(0x07.toByte)
+ arr(8) should ===(0x08.toByte)
+ arr(9) should ===(0.toByte)
+ }
+ "putShort" in {
+ val arr = new Array[Byte](4)
+
+ SWARUtil.putShort(arr, 0, 0x0102, ByteOrder.BIG_ENDIAN)
+ arr.take(2).toSeq should ===(Seq[Byte](0x01, 0x02))
+
+ SWARUtil.putShort(arr, 2, 0x0304, ByteOrder.BIG_ENDIAN)
+ arr.drop(2).toSeq should ===(Seq[Byte](0x03, 0x04))
+
+ SWARUtil.putShort(arr, 0, 0x0102, ByteOrder.LITTLE_ENDIAN)
+ arr.take(2).toSeq should ===(Seq[Byte](0x02, 0x01))
+
+ SWARUtil.putShort(arr, 2, 0x0304, ByteOrder.LITTLE_ENDIAN)
+ arr.drop(2).toSeq should ===(Seq[Byte](0x04, 0x03))
+ }
+ "putShortBEWithoutMethodHandle" in {
+ val arr = new Array[Byte](4)
+
+ SWARUtil.putShortBEWithoutMethodHandle(arr, 0, 0x0102)
+ arr.take(2).toSeq should ===(Seq[Byte](0x01, 0x02))
+
+ SWARUtil.putShortBEWithoutMethodHandle(arr, 2, 0x0304)
+ arr.drop(2).toSeq should ===(Seq[Byte](0x03, 0x04))
+
+ // round-trip
+ SWARUtil.putShortBEWithoutMethodHandle(arr, 0, Short.MaxValue)
+ SWARUtil.getShortBEWithoutMethodHandle(arr, 0) should ===(Short.MaxValue)
+ SWARUtil.putShortBEWithoutMethodHandle(arr, 0, Short.MinValue)
+ SWARUtil.getShortBEWithoutMethodHandle(arr, 0) should ===(Short.MinValue)
+ }
+ "putShortLEWithoutMethodHandle" in {
+ val arr = new Array[Byte](4)
+
+ SWARUtil.putShortLEWithoutMethodHandle(arr, 0, 0x0102)
+ arr.take(2).toSeq should ===(Seq[Byte](0x02, 0x01))
+
+ SWARUtil.putShortLEWithoutMethodHandle(arr, 2, 0x0304)
+ arr.drop(2).toSeq should ===(Seq[Byte](0x04, 0x03))
+
+ // round-trip
+ SWARUtil.putShortLEWithoutMethodHandle(arr, 0, Short.MaxValue)
+ SWARUtil.getShortLEWithoutMethodHandle(arr, 0) should ===(Short.MaxValue)
+ SWARUtil.putShortLEWithoutMethodHandle(arr, 0, Short.MinValue)
+ SWARUtil.getShortLEWithoutMethodHandle(arr, 0) should ===(Short.MinValue)
+ }
+ "putShort and getShort are inverses" in {
+ val arr = new Array[Byte](2)
+ for (value <- Seq(0.toShort, 1.toShort, (-1).toShort, Short.MaxValue,
Short.MinValue, 0x0102.toShort)) {
+ SWARUtil.putShort(arr, 0, value, ByteOrder.BIG_ENDIAN)
+ SWARUtil.getShort(arr, 0, ByteOrder.BIG_ENDIAN) should ===(value)
+ SWARUtil.putShort(arr, 0, value, ByteOrder.LITTLE_ENDIAN)
+ SWARUtil.getShort(arr, 0, ByteOrder.LITTLE_ENDIAN) should ===(value)
+ }
+ }
+ "putShort writes at correct offset" in {
+ val arr = new Array[Byte](4)
+ SWARUtil.putShort(arr, 1, 0x0102, ByteOrder.BIG_ENDIAN)
+ arr(0) should ===(0.toByte)
+ arr(1) should ===(0x01.toByte)
+ arr(2) should ===(0x02.toByte)
+ arr(3) should ===(0.toByte)
+ }
}
}
diff --git a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
index a46e383334..43735db440 100644
--- a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
@@ -2244,32 +2244,21 @@ final class ByteStringBuilder extends Builder[Byte,
ByteString] {
* Add a single Short to this builder.
*/
def putShort(x: Int)(implicit byteOrder: ByteOrder): this.type = {
- if (byteOrder == ByteOrder.BIG_ENDIAN) {
- this += (x >>> 8).toByte
- this += (x >>> 0).toByte
- } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- this += (x >>> 0).toByte
- this += (x >>> 8).toByte
- } else throw new IllegalArgumentException("Unknown byte order " +
byteOrder)
+ ensureTempSize(_tempLength + 2)
+ SWARUtil.putShort(_temp, _tempLength, x, byteOrder)
+ _tempLength += 2
+ _length += 2
+ this
}
/**
* Add a single Int to this builder.
*/
def putInt(x: Int)(implicit byteOrder: ByteOrder): this.type = {
- fillArray(4) { (target, offset) =>
- if (byteOrder == ByteOrder.BIG_ENDIAN) {
- target(offset + 0) = (x >>> 24).toByte
- target(offset + 1) = (x >>> 16).toByte
- target(offset + 2) = (x >>> 8).toByte
- target(offset + 3) = (x >>> 0).toByte
- } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- target(offset + 0) = (x >>> 0).toByte
- target(offset + 1) = (x >>> 8).toByte
- target(offset + 2) = (x >>> 16).toByte
- target(offset + 3) = (x >>> 24).toByte
- } else throw new IllegalArgumentException("Unknown byte order " +
byteOrder)
- }
+ ensureTempSize(_tempLength + 4)
+ SWARUtil.putInt(_temp, _tempLength, x, byteOrder)
+ _tempLength += 4
+ _length += 4
this
}
@@ -2277,27 +2266,10 @@ final class ByteStringBuilder extends Builder[Byte,
ByteString] {
* Add a single Long to this builder.
*/
def putLong(x: Long)(implicit byteOrder: ByteOrder): this.type = {
- fillArray(8) { (target, offset) =>
- if (byteOrder == ByteOrder.BIG_ENDIAN) {
- target(offset + 0) = (x >>> 56).toByte
- target(offset + 1) = (x >>> 48).toByte
- target(offset + 2) = (x >>> 40).toByte
- target(offset + 3) = (x >>> 32).toByte
- target(offset + 4) = (x >>> 24).toByte
- target(offset + 5) = (x >>> 16).toByte
- target(offset + 6) = (x >>> 8).toByte
- target(offset + 7) = (x >>> 0).toByte
- } else if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- target(offset + 0) = (x >>> 0).toByte
- target(offset + 1) = (x >>> 8).toByte
- target(offset + 2) = (x >>> 16).toByte
- target(offset + 3) = (x >>> 24).toByte
- target(offset + 4) = (x >>> 32).toByte
- target(offset + 5) = (x >>> 40).toByte
- target(offset + 6) = (x >>> 48).toByte
- target(offset + 7) = (x >>> 56).toByte
- } else throw new IllegalArgumentException("Unknown byte order " +
byteOrder)
- }
+ ensureTempSize(_tempLength + 8)
+ SWARUtil.putLong(_temp, _tempLength, x, byteOrder)
+ _tempLength += 8
+ _length += 8
this
}
diff --git a/actor/src/main/scala/org/apache/pekko/util/SWARUtil.scala
b/actor/src/main/scala/org/apache/pekko/util/SWARUtil.scala
index ea65ddd683..7dc9d70d58 100644
--- a/actor/src/main/scala/org/apache/pekko/util/SWARUtil.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/SWARUtil.scala
@@ -235,6 +235,84 @@ private[pekko] object SWARUtil {
}
}
+ /**
+ * Writes an int value at the specified index in the given byte array.
+ * Uses a VarHandle byte array view if supported, otherwise falls back to
byte-by-byte writes.
+ * Does not range check - assumes caller has checked bounds.
+ *
+ * @param array the byte array to write to
+ * @param index the index to write at
+ * @param value the int value to write
+ * @param byteOrder the byte order to use (big-endian or little-endian)
+ */
+ def putInt(array: Array[Byte], index: Int, value: Int, byteOrder:
ByteOrder): Unit = {
+ if (byteOrder == ByteOrder.BIG_ENDIAN) {
+ if (intBeArrayViewSupported) {
+ intBeArrayView.set(array, index, value)
+ } else {
+ putIntBEWithoutMethodHandle(array, index, value)
+ }
+ } else {
+ if (intLeArrayViewSupported) {
+ intLeArrayView.set(array, index, value)
+ } else {
+ putIntLEWithoutMethodHandle(array, index, value)
+ }
+ }
+ }
+
+ /**
+ * Writes the low 16 bits of `value` at the specified index in the given
byte array.
+ * Uses a VarHandle byte array view if supported, otherwise falls back to
byte-by-byte writes.
+ * Does not range check - assumes caller has checked bounds.
+ *
+ * @param array the byte array to write to
+ * @param index the index to write at
+ * @param value the value whose low 16 bits are written
+ * @param byteOrder the byte order to use (big-endian or little-endian)
+ */
+ def putShort(array: Array[Byte], index: Int, value: Int, byteOrder:
ByteOrder): Unit = {
+ if (byteOrder == ByteOrder.BIG_ENDIAN) {
+ if (shortBeArrayViewSupported) {
+ shortBeArrayView.set(array, index, value.toShort)
+ } else {
+ putShortBEWithoutMethodHandle(array, index, value)
+ }
+ } else {
+ if (shortLeArrayViewSupported) {
+ shortLeArrayView.set(array, index, value.toShort)
+ } else {
+ putShortLEWithoutMethodHandle(array, index, value)
+ }
+ }
+ }
+
+ /**
+ * Writes a long value at the specified index in the given byte array.
+ * Uses a VarHandle byte array view if supported, otherwise falls back to
byte-by-byte writes.
+ * Does not range check - assumes caller has checked bounds.
+ *
+ * @param array the byte array to write to
+ * @param index the index to write at
+ * @param value the long value to write
+ * @param byteOrder the byte order to use (big-endian or little-endian)
+ */
+ def putLong(array: Array[Byte], index: Int, value: Long, byteOrder:
ByteOrder): Unit = {
+ if (byteOrder == ByteOrder.BIG_ENDIAN) {
+ if (longBeArrayViewSupported) {
+ longBeArrayView.set(array, index, value)
+ } else {
+ putLongBEWithoutMethodHandle(array, index, value)
+ }
+ } else {
+ if (longLeArrayViewSupported) {
+ longLeArrayView.set(array, index, value)
+ } else {
+ putLongLEWithoutMethodHandle(array, index, value)
+ }
+ }
+ }
+
// Fallback implementations for environments that do not support
MethodHandles.byteArrayViewVarHandle
private[pekko] def getLongBEWithoutMethodHandle(array: Array[Byte], index:
Int): Long = {
@@ -279,4 +357,50 @@ private[pekko] object SWARUtil {
private[pekko] def getShortLEWithoutMethodHandle(array: Array[Byte], index:
Int): Short =
((array(index) & 0xFF) | (array(index + 1) & 0xFF) << 8).toShort
+ private[pekko] def putIntBEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Int): Unit = {
+ array(index) = (value >>> 24).toByte
+ array(index + 1) = (value >>> 16).toByte
+ array(index + 2) = (value >>> 8).toByte
+ array(index + 3) = value.toByte
+ }
+
+ private[pekko] def putIntLEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Int): Unit = {
+ array(index) = value.toByte
+ array(index + 1) = (value >>> 8).toByte
+ array(index + 2) = (value >>> 16).toByte
+ array(index + 3) = (value >>> 24).toByte
+ }
+
+ private[pekko] def putShortBEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Int): Unit = {
+ array(index) = (value >>> 8).toByte
+ array(index + 1) = value.toByte
+ }
+
+ private[pekko] def putShortLEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Int): Unit = {
+ array(index) = value.toByte
+ array(index + 1) = (value >>> 8).toByte
+ }
+
+ private[pekko] def putLongBEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Long): Unit = {
+ array(index) = (value >>> 56).toByte
+ array(index + 1) = (value >>> 48).toByte
+ array(index + 2) = (value >>> 40).toByte
+ array(index + 3) = (value >>> 32).toByte
+ array(index + 4) = (value >>> 24).toByte
+ array(index + 5) = (value >>> 16).toByte
+ array(index + 6) = (value >>> 8).toByte
+ array(index + 7) = value.toByte
+ }
+
+ private[pekko] def putLongLEWithoutMethodHandle(array: Array[Byte], index:
Int, value: Long): Unit = {
+ array(index) = value.toByte
+ array(index + 1) = (value >>> 8).toByte
+ array(index + 2) = (value >>> 16).toByte
+ array(index + 3) = (value >>> 24).toByte
+ array(index + 4) = (value >>> 32).toByte
+ array(index + 5) = (value >>> 40).toByte
+ array(index + 6) = (value >>> 48).toByte
+ array(index + 7) = (value >>> 56).toByte
+ }
+
}
diff --git
a/bench-jmh/src/main/scala/org/apache/pekko/util/ByteString_putInt_Benchmark.scala
b/bench-jmh/src/main/scala/org/apache/pekko/util/ByteString_putInt_Benchmark.scala
new file mode 100644
index 0000000000..74b0b4d992
--- /dev/null
+++
b/bench-jmh/src/main/scala/org/apache/pekko/util/ByteString_putInt_Benchmark.scala
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+/*
+ * Copyright (C) 2014-2022 Lightbend Inc. <https://www.lightbend.com>
+ */
+
+package org.apache.pekko.util
+
+import java.util.concurrent.TimeUnit
+
+import org.openjdk.jmh.annotations._
+import org.openjdk.jmh.infra.Blackhole
+
+@State(Scope.Benchmark)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@BenchmarkMode(Array(Mode.Throughput))
+@Fork(2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+class ByteString_putInt_Benchmark {
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putShortBE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0
+ while (i < 1000) {
+ builder.putShort(i)(java.nio.ByteOrder.BIG_ENDIAN)
+ i += 1
+ }
+ bh.consume(builder.result())
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putShortLE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0
+ while (i < 1000) {
+ builder.putShort(i)(java.nio.ByteOrder.LITTLE_ENDIAN)
+ i += 1
+ }
+ bh.consume(builder.result())
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putIntBE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0
+ while (i < 1000) {
+ builder.putInt(i)(java.nio.ByteOrder.BIG_ENDIAN)
+ i += 1
+ }
+ bh.consume(builder.result())
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putIntLE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0
+ while (i < 1000) {
+ builder.putInt(i)(java.nio.ByteOrder.LITTLE_ENDIAN)
+ i += 1
+ }
+ bh.consume(builder.result())
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putLongBE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0L
+ while (i < 1000L) {
+ builder.putLong(i)(java.nio.ByteOrder.BIG_ENDIAN)
+ i += 1L
+ }
+ bh.consume(builder.result())
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(1000)
+ def putLongLE(bh: Blackhole): Unit = {
+ val builder = ByteString.newBuilder
+ var i = 0L
+ while (i < 1000L) {
+ builder.putLong(i)(java.nio.ByteOrder.LITTLE_ENDIAN)
+ i += 1L
+ }
+ bh.consume(builder.result())
+ }
+
+}
diff --git
a/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
b/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
index 200c835ff6..3539ce85fb 100644
---
a/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
+++
b/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
@@ -13,7 +13,7 @@
package org.apache.pekko.persistence.serialization
-import java.io._
+import java.io.NotSerializableException
import java.nio.ByteOrder
import org.apache.pekko
@@ -81,13 +81,18 @@ class SnapshotSerializer(val system: ExtendedActorSystem)
extends BaseSerializer
Snapshot(snapshotFromBinary(bytes))
private def headerToBinary(snapshot: AnyRef, snapshotSerializer:
Serializer): Array[Byte] = {
- val out = new ByteArrayOutputStream
- writeInt(out, snapshotSerializer.identifier)
-
val ms =
migrateManifestIfNecessary(Serializers.manifestFor(snapshotSerializer,
snapshot))
- if (ms.nonEmpty) out.write(ms.getBytes(UTF_8))
-
- out.toByteArray
+ if (ms.isEmpty) {
+ val result = new Array[Byte](4)
+ SWARUtil.putInt(result, 0, snapshotSerializer.identifier,
ByteOrder.LITTLE_ENDIAN)
+ result
+ } else {
+ val manifestBytes = ms.getBytes(UTF_8)
+ val result = new Array[Byte](4 + manifestBytes.length)
+ SWARUtil.putInt(result, 0, snapshotSerializer.identifier,
ByteOrder.LITTLE_ENDIAN)
+ System.arraycopy(manifestBytes, 0, result, 4, manifestBytes.length)
+ result
+ }
}
private def headerFromBinary(bytes: Array[Byte]): (Int, String) = {
@@ -146,14 +151,13 @@ class SnapshotSerializer(val system: ExtendedActorSystem)
extends BaseSerializer
val snapshotSerializer = serialization.findSerializerFor(snapshot)
val headerBytes = headerToBinary(snapshot, snapshotSerializer)
+ val snapshotBytes = snapshotSerializer.toBinary(snapshot)
- val out = new ByteArrayOutputStream
-
- writeInt(out, headerBytes.length)
-
- out.write(headerBytes)
- out.write(snapshotSerializer.toBinary(snapshot))
- out.toByteArray
+ val result = new Array[Byte](4 + headerBytes.length +
snapshotBytes.length)
+ SWARUtil.putInt(result, 0, headerBytes.length, ByteOrder.LITTLE_ENDIAN)
+ System.arraycopy(headerBytes, 0, result, 4, headerBytes.length)
+ System.arraycopy(snapshotBytes, 0, result, 4 + headerBytes.length,
snapshotBytes.length)
+ result
}
val oldInfo = Serialization.currentTransportInformation.value
@@ -176,10 +180,4 @@ class SnapshotSerializer(val system: ExtendedActorSystem)
extends BaseSerializer
.get
}
- private def writeInt(out: OutputStream, i: Int): Unit = {
- out.write(i >>> 0)
- out.write(i >>> 8)
- out.write(i >>> 16)
- out.write(i >>> 24)
- }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]