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 82979c2d0d5..43eb962ca27 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 @@ -3312,8 +3312,16 @@ namespace Akka.IO public static Akka.IO.ByteString CopyFrom(byte[] array) { } public static Akka.IO.ByteString CopyFrom(System.ArraySegment<byte> buffer) { } public static Akka.IO.ByteString CopyFrom(byte[] array, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span, int offset, int count) { } public static Akka.IO.ByteString CopyFrom(System.Collections.Generic.IEnumerable<System.ArraySegment<byte>> buffers) { } public int CopyTo(byte[] buffer, int index, int count) { } + public int CopyTo(ref System.Memory<byte> buffer) { } + public int CopyTo(ref System.Memory<byte> buffer, int index, int count) { } + public int CopyTo(ref System.Span<byte> buffer) { } + public int CopyTo(ref System.Span<byte> buffer, int index, int count) { } public override bool Equals(object obj) { } public bool Equals(Akka.IO.ByteString other) { } public static Akka.IO.ByteString FromBytes(byte[] array) { } 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 61cd0b8994d..d3eb1bb8107 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 @@ -3319,8 +3319,16 @@ namespace Akka.IO public static Akka.IO.ByteString CopyFrom(byte[] array) { } public static Akka.IO.ByteString CopyFrom(System.ArraySegment<byte> buffer) { } public static Akka.IO.ByteString CopyFrom(byte[] array, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span, int offset, int count) { } public static Akka.IO.ByteString CopyFrom(System.Collections.Generic.IEnumerable<System.ArraySegment<byte>> buffers) { } public int CopyTo(byte[] buffer, int index, int count) { } + public int CopyTo(ref System.Memory<byte> buffer) { } + public int CopyTo(ref System.Memory<byte> buffer, int index, int count) { } + public int CopyTo(ref System.Span<byte> buffer) { } + public int CopyTo(ref System.Span<byte> buffer, int index, int count) { } public override bool Equals(object obj) { } public bool Equals(Akka.IO.ByteString other) { } public static Akka.IO.ByteString FromBytes(byte[] array) { } 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 82979c2d0d5..43eb962ca27 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 @@ -3312,8 +3312,16 @@ namespace Akka.IO public static Akka.IO.ByteString CopyFrom(byte[] array) { } public static Akka.IO.ByteString CopyFrom(System.ArraySegment<byte> buffer) { } public static Akka.IO.ByteString CopyFrom(byte[] array, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory) { } + public static Akka.IO.ByteString CopyFrom(System.Memory<byte> memory, int offset, int count) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span) { } + public static Akka.IO.ByteString CopyFrom(System.Span<byte> span, int offset, int count) { } public static Akka.IO.ByteString CopyFrom(System.Collections.Generic.IEnumerable<System.ArraySegment<byte>> buffers) { } public int CopyTo(byte[] buffer, int index, int count) { } + public int CopyTo(ref System.Memory<byte> buffer) { } + public int CopyTo(ref System.Memory<byte> buffer, int index, int count) { } + public int CopyTo(ref System.Span<byte> buffer) { } + public int CopyTo(ref System.Span<byte> buffer, int index, int count) { } public override bool Equals(object obj) { } public bool Equals(Akka.IO.ByteString other) { } public static Akka.IO.ByteString FromBytes(byte[] array) { } diff --git a/src/core/Akka.Tests/Util/ByteStringSpec.cs b/src/core/Akka.Tests/Util/ByteStringSpec.cs index d470b8c538e..37fe0543385 100644 --- a/src/core/Akka.Tests/Util/ByteStringSpec.cs +++ b/src/core/Akka.Tests/Util/ByteStringSpec.cs @@ -5,7 +5,6 @@ // </copyright> //----------------------------------------------------------------------- -using System; using System.Linq; using System.Text; using Akka.IO; @@ -188,5 +187,27 @@ public void A_sliced_ByteString_must_return_correct_string_for_ToString() Assert.Equal(expectedLeft, actualLeft); Assert.Equal(expectedRight, actualRight); } + +#if !NETFRAMEWORK + [Fact(DisplayName = "A sliced byte string using Range must return the correct string for ToString")] + public void A_sliced_ByteString_using_Range_must_return_correct_string_for_ToString() + { + const string expected = "ABCDEF"; + Encoding encoding = Encoding.ASCII; + + int halfExpected = expected.Length / 2; + + string expectedLeft = expected.Substring(startIndex: 0, length: halfExpected); + string expectedRight = expected.Substring(startIndex: halfExpected, length: halfExpected); + + ByteString data = ByteString.FromString(expected, encoding); + + string actualLeft = data[..halfExpected].ToString(encoding); + string actualRight = data[halfExpected..].ToString(encoding); + + Assert.Equal(expectedLeft, actualLeft); + Assert.Equal(expectedRight, actualRight); + } +#endif } } diff --git a/src/core/Akka/Util/ByteString.cs b/src/core/Akka/Util/ByteString.cs index bc7c8933cf5..50aee01ceb1 100644 --- a/src/core/Akka/Util/ByteString.cs +++ b/src/core/Akka/Util/ByteString.cs @@ -76,6 +76,62 @@ public static ByteString CopyFrom(byte[] array, int offset, int count) return new ByteString(copy, 0, copy.Length); } + /// <summary> + /// Creates a new <see cref="ByteString"/> by copying a <see cref="Memory{T}"/>. + /// </summary> + /// <param name="memory">The <see cref="Memory{T}"/> to copy</param> + /// <returns>The new <see cref="ByteString"/></returns> + public static ByteString CopyFrom(Memory<byte> memory) + => CopyFrom(memory, 0, memory.Length); + + /// <summary> + /// Creates a new <see cref="ByteString"/> by copying a <see cref="Memory{T}"/>. + /// </summary> + /// <param name="memory">The <see cref="Memory{T}"/> to copy</param> + /// <param name="offset">Index in provided <paramref name="memory"/>, at which copy should start.</param> + /// <param name="count">Number of bytes to copy.</param> + /// <returns>The new <see cref="ByteString"/></returns> + public static ByteString CopyFrom(Memory<byte> memory, int offset, int count) + { + if (count == 0) return Empty; + + if (offset < 0 || offset >= memory.Length) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset of [{offset}] is outside bounds of an array [{memory.Length}]"); + if (count > memory.Length - offset) throw new ArgumentException($"Provided length [{count}] of array to copy doesn't fit array length [{memory.Length}] within given offset [{offset}]", nameof(count)); + + var copy = new byte[count]; + memory.Slice(offset, count).CopyTo(copy); + + return new ByteString(copy, 0, copy.Length); + } + + /// <summary> + /// Creates a new <see cref="ByteString"/> by copying a <see cref="Span{T}"/>. + /// </summary> + /// <param name="span">The <see cref="Span{T}"/> to copy</param> + /// <returns>The new <see cref="ByteString"/></returns> + public static ByteString CopyFrom(Span<byte> span) + => CopyFrom(span, 0, span.Length); + + /// <summary> + /// Creates a new <see cref="ByteString"/> by copying a <see cref="Span{T}"/>. + /// </summary> + /// <param name="span">The <see cref="Span{T}"/> to copy</param> + /// <param name="offset">Index in provided <paramref name="span"/>, at which copy should start.</param> + /// <param name="count">Number of bytes to copy.</param> + /// <returns>The new <see cref="ByteString"/></returns> + public static ByteString CopyFrom(Span<byte> span, int offset, int count) + { + if (count == 0) return Empty; + + if (offset < 0 || offset >= span.Length) throw new ArgumentOutOfRangeException(nameof(offset), $"Provided offset of [{offset}] is outside bounds of an array [{span.Length}]"); + if (count > span.Length - offset) throw new ArgumentException($"Provided length [{count}] of array to copy doesn't fit array length [{span.Length}] within given offset [{offset}]", nameof(count)); + + var copy = new byte[count]; + span.Slice(offset, count).CopyTo(copy); + + return new ByteString(copy, 0, copy.Length); + } + /// <summary> /// Creates a new <see cref="ByteString"/> by copying segments of bytes. /// </summary> @@ -502,6 +558,82 @@ public int CopyTo(byte[] buffer, int index, int count) return 0; } + /// <summary> + /// Copies content of a current <see cref="ByteString"/> into a provided <see cref="Memory{T}"/> + /// <paramref name="buffer"/> + /// </summary> + /// <returns>The number of bytes copied</returns> + public int CopyTo(ref Memory<byte> buffer) + => CopyTo(ref buffer, 0, buffer.Length); + + /// <summary> + /// Copies content of a current <see cref="ByteString"/> into a provided <see cref="Memory{T}"/> + /// <paramref name="buffer"/> starting from <paramref name="index"/> in that + /// buffer and copying a <paramref name="count"/> number of bytes. + /// </summary> + /// <returns>The number of bytes copied</returns> + public int CopyTo(ref Memory<byte> buffer, int index, int count) + { + if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException(nameof(index), "Provided index is outside the bounds of the buffer to copy to."); + if (count > buffer.Length - index) throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", nameof(count)); + + count = Math.Min(count, _count); + var remaining = count; + var position = index; + foreach (var b in _buffers) + { + var toCopy = Math.Min(b.Count, remaining); + + var bufferSpan = buffer.Span.Slice(position, toCopy); + b.AsSpan().CopyTo(bufferSpan); + + position += toCopy; + remaining -= toCopy; + + if (remaining == 0) return count; + } + + return 0; + } + + /// <summary> + /// Copies content of a current <see cref="ByteString"/> into a provided <see cref="Span{T}"/> + /// <paramref name="buffer"/>. + /// </summary> + /// <returns>The number of bytes copied</returns> + public int CopyTo(ref Span<byte> buffer) + => CopyTo(ref buffer, 0, buffer.Length); + + /// <summary> + /// Copies content of a current <see cref="ByteString"/> into a provided <see cref="Span{T}"/> + /// <paramref name="buffer"/> starting from <paramref name="index"/> in that + /// buffer and copying a <paramref name="count"/> number of bytes. + /// </summary> + /// <returns>The number of bytes copied</returns> + public int CopyTo(ref Span<byte> buffer, int index, int count) + { + if (index < 0 || index >= buffer.Length) throw new ArgumentOutOfRangeException(nameof(index), "Provided index is outside the bounds of the buffer to copy to."); + if (count > buffer.Length - index) throw new ArgumentException("Provided number of bytes to copy won't fit into provided buffer", nameof(count)); + + count = Math.Min(count, _count); + var remaining = count; + var position = index; + foreach (var b in _buffers) + { + var toCopy = Math.Min(b.Count, remaining); + + var bufferSpan = buffer.Slice(position, toCopy); + b.AsSpan().CopyTo(bufferSpan); + + position += toCopy; + remaining -= toCopy; + + if (remaining == 0) return count; + } + + return 0; + } + /// <summary> /// Copies content of the current <see cref="ByteString"/> to a provided /// writeable <paramref name="stream"/>.