Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to ByteString for copying to/from Memory and Span #6026

Original file line number Diff line number Diff line change
Expand Up @@ -3305,8 +3305,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) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3305,8 +3305,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) { }
Expand Down
23 changes: 22 additions & 1 deletion src/core/Akka.Tests/Util/ByteStringSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using System.Text;
using Akka.IO;
Expand Down Expand Up @@ -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
}
}
132 changes: 132 additions & 0 deletions src/core/Akka/Util/ByteString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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"/>.
Expand Down