From 5e7756bf6869ebcd4a5fdc883b1e71b8dd4eb652 Mon Sep 17 00:00:00 2001 From: Lydon Chandra Date: Fri, 24 Jan 2025 16:00:59 +0800 Subject: [PATCH 1/5] add ToReadOnlySpan --- src/core/Akka/Util/ByteString.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/core/Akka/Util/ByteString.cs b/src/core/Akka/Util/ByteString.cs index 096725579d8..46436cfc747 100644 --- a/src/core/Akka/Util/ByteString.cs +++ b/src/core/Akka/Util/ByteString.cs @@ -507,6 +507,27 @@ public byte[] ToArray() CopyTo(copy, 0, _count); return copy; } + + /// + /// Returns a ReadOnlySpan over the contents of this ByteString. + /// This is a non-copying operation, when the ByteString is compact. + /// When it is not compact, the contents are copied into a new array. + /// + /// A ReadOnlySpan over the byte data. + public ReadOnlySpan ToReadOnlySpan() + { + if (_count == 0) + return ReadOnlySpan.Empty; + + if (IsCompact) + { + // If compact, data is in a single buffer. + var compactBuffer = _buffers[0]; + return new ReadOnlySpan(compactBuffer.Array, compactBuffer.Offset, compactBuffer.Count); + } + + return new ReadOnlySpan(ToArray()); + } /// /// Appends at the tail From b824b7276f0ebf81c6e787ca389dbe41dd01eeab Mon Sep 17 00:00:00 2001 From: Lydon Chandra Date: Tue, 28 Jan 2025 08:55:19 +0800 Subject: [PATCH 2/5] Tests for ByteString ToReadOnlySpan --- src/core/Akka.Tests/Util/ByteStringSpec.cs | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/core/Akka.Tests/Util/ByteStringSpec.cs b/src/core/Akka.Tests/Util/ByteStringSpec.cs index 9605b2698c5..7cdee66d4a5 100644 --- a/src/core/Akka.Tests/Util/ByteStringSpec.cs +++ b/src/core/Akka.Tests/Util/ByteStringSpec.cs @@ -5,9 +5,12 @@ // //----------------------------------------------------------------------- +using System; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using Akka.IO; +using Akka.TestKit; using FluentAssertions; using FsCheck; using Xunit; @@ -42,6 +45,59 @@ public void A_ByteString_must_have_correct_size_when_concatenating() .QuickCheckThrowOnFailure(); } + [Fact] + public void A_ByteString_ToReadOnlySpan_must_have_correct_size() + { + Prop.ForAll((ByteString a, ByteString b) => + { + a.ToReadOnlySpan().Length.Should().Be(a.Count); + b.ToReadOnlySpan().Length.Should().Be(b.Count); + var concat = a + b; + var spanConcat = concat.ToReadOnlySpan(); + return spanConcat.Length == concat.Count; + }).QuickCheckThrowOnFailure(); + } + + [Fact] + public void A_ByteString_Compact_ToReadOnlySpan_must_have_identical_memory_ref() + { + var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var a = ByteString.FromBytes(bytes); + var aSpan = a.ToReadOnlySpan(); + + var memory = new ReadOnlyMemory(bytes); + var spanMemory = memory.Span; + + // span has memory reference to underlying byte array + (aSpan == spanMemory).Should().BeTrue(); + + // Do another ToReadOnlySpan + var span2 = a.ToReadOnlySpan(); + (aSpan == span2).Should().BeTrue(); + } + + [Fact] + public void A_ByteString_ToReadOnlySpan_compacted_must_have_identical_memory_ref() + { + var bytesA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var bytesB = new byte[] { 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + var a = ByteString.FromBytes(bytesA); + var b = ByteString.FromBytes(bytesB); + + var concat = a + b; + + var concatSpan = concat.ToReadOnlySpan(); + var concatSpan2 = concat.ToReadOnlySpan(); + // not compact, returns a new span + (concatSpan == concatSpan2).Should().BeFalse(); + + // compact, will return same span + var compacted = concat.Compact(); + var concatSpan3 = compacted.ToReadOnlySpan(); + var concatSpan4 = compacted.ToReadOnlySpan(); + (concatSpan3 == concatSpan4).Should().BeTrue(); + } + [Fact] public void A_ByteString_must_have_correct_size_when_slicing_from_index() { From a02ef3be1823eb2ca762210025f4405e260dd340 Mon Sep 17 00:00:00 2001 From: Lydon Chandra Date: Tue, 28 Jan 2025 11:08:59 +0800 Subject: [PATCH 3/5] Add ToReadOnlySpan to CoreApiSpec --- .../verify/CoreAPISpec.ApproveCore.DotNet.verified.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt index 104827f2cf0..c8dcb164155 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt @@ -3796,6 +3796,7 @@ namespace Akka.IO public Akka.IO.ByteString Slice(int index) { } public Akka.IO.ByteString Slice(int index, int count) { } public byte[] ToArray() { } + public System.ReadOnlySpan ToReadOnlySpan() { } public override string ToString() { } public string ToString(System.Text.Encoding encoding) { } public void WriteTo(System.IO.Stream stream) { } From ba139a28459e6441fc5e69a358ac0f486ae3bce8 Mon Sep 17 00:00:00 2001 From: Lydon Chandra Date: Thu, 30 Jan 2025 09:27:45 +0800 Subject: [PATCH 4/5] fixed api tests --- .../verify/CoreAPISpec.ApproveCore.Core.verified.txt | 1 + .../verify/CoreAPISpec.ApproveCore.Net.verified.txt | 1 + .../Akka.API.Tests/verify/CoreAPISpec.ApproveCore.verified.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Core.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Core.verified.txt index df6b1461b8e..d0a6f9ed54c 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Core.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Core.verified.txt @@ -3378,6 +3378,7 @@ namespace Akka.IO public Akka.IO.ByteString Slice(int index) { } public Akka.IO.ByteString Slice(int index, int count) { } public byte[] ToArray() { } + public System.ReadOnlySpan ToReadOnlySpan() { } public override string ToString() { } public string ToString(System.Text.Encoding encoding) { } public void WriteTo(System.IO.Stream stream) { } diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt index 9b8a28e3d35..d1553938cc4 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt @@ -3786,6 +3786,7 @@ namespace Akka.IO public Akka.IO.ByteString Slice(int index) { } public Akka.IO.ByteString Slice(int index, int count) { } public byte[] ToArray() { } + public System.ReadOnlySpan ToReadOnlySpan() { } public override string ToString() { } public string ToString(System.Text.Encoding encoding) { } public void WriteTo(System.IO.Stream stream) { } diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.verified.txt index 280340c6f21..8bdae5a5617 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.verified.txt @@ -3341,6 +3341,7 @@ namespace Akka.IO public Akka.IO.ByteString Slice(int index) { } public Akka.IO.ByteString Slice(int index, int count) { } public byte[] ToArray() { } + public System.ReadOnlySpan ToReadOnlySpan() { } public override string ToString() { } public string ToString(System.Text.Encoding encoding) { } public void WriteTo(System.IO.Stream stream) { } From a308d19ea7507fc52ebfc11430b0dfe0cfd5c2e6 Mon Sep 17 00:00:00 2001 From: Lydon Chandra Date: Thu, 30 Jan 2025 13:10:53 +0800 Subject: [PATCH 5/5] trigger test --- src/core/Akka.Tests/Util/ByteStringSpec.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka.Tests/Util/ByteStringSpec.cs b/src/core/Akka.Tests/Util/ByteStringSpec.cs index 7cdee66d4a5..053cf6bbd43 100644 --- a/src/core/Akka.Tests/Util/ByteStringSpec.cs +++ b/src/core/Akka.Tests/Util/ByteStringSpec.cs @@ -68,7 +68,7 @@ public void A_ByteString_Compact_ToReadOnlySpan_must_have_identical_memory_ref() var memory = new ReadOnlyMemory(bytes); var spanMemory = memory.Span; - // span has memory reference to underlying byte array + // span has memory reference to underlying byte array (aSpan == spanMemory).Should().BeTrue(); // Do another ToReadOnlySpan