Skip to content

Commit

Permalink
Add ByteString ToReadOnlySpan (#7487)
Browse files Browse the repository at this point in the history
* add ToReadOnlySpan

* Tests for ByteString ToReadOnlySpan

* Add ToReadOnlySpan to CoreApiSpec

* fixed api tests

* trigger test
  • Loading branch information
fmg-lydonchandra authored Jan 30, 2025
1 parent ea2d195 commit c8ebf77
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> ToReadOnlySpan() { }
public override string ToString() { }
public string ToString(System.Text.Encoding encoding) { }
public void WriteTo(System.IO.Stream stream) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> ToReadOnlySpan() { }
public override string ToString() { }
public string ToString(System.Text.Encoding encoding) { }
public void WriteTo(System.IO.Stream stream) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> ToReadOnlySpan() { }
public override string ToString() { }
public string ToString(System.Text.Encoding encoding) { }
public void WriteTo(System.IO.Stream stream) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> ToReadOnlySpan() { }
public override string ToString() { }
public string ToString(System.Text.Encoding encoding) { }
public void WriteTo(System.IO.Stream stream) { }
Expand Down
56 changes: 56 additions & 0 deletions src/core/Akka.Tests/Util/ByteStringSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Akka.IO;
using Akka.TestKit;
using FluentAssertions;
using FsCheck;
using Xunit;
Expand Down Expand Up @@ -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<byte>(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()
{
Expand Down
21 changes: 21 additions & 0 deletions src/core/Akka/Util/ByteString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,27 @@ public byte[] ToArray()
CopyTo(copy, 0, _count);
return copy;
}

/// <summary>
/// Returns a ReadOnlySpan<byte/> 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.
/// </summary>
/// <returns>A ReadOnlySpan<byte/> over the byte data.</returns>
public ReadOnlySpan<byte> ToReadOnlySpan()
{
if (_count == 0)
return ReadOnlySpan<byte>.Empty;

if (IsCompact)
{
// If compact, data is in a single buffer.
var compactBuffer = _buffers[0];
return new ReadOnlySpan<byte>(compactBuffer.Array, compactBuffer.Offset, compactBuffer.Count);
}

return new ReadOnlySpan<byte>(ToArray());
}

/// <summary>
/// Appends <paramref name="other"/> <see cref="ByteString"/> at the tail
Expand Down

0 comments on commit c8ebf77

Please sign in to comment.