Skip to content

Commit

Permalink
Add support to ByteString for copying to/from Memory and Span (#…
Browse files Browse the repository at this point in the history
…6026)

* Add support to `ByteString` for copying to/from `Memory` and `Span`

* Update API Verify list

Co-authored-by: Aaron Stannard <aaron@petabridge.com>
  • Loading branch information
Arkatufus and Aaronontheweb committed Jan 3, 2023
1 parent 4d124ce commit 965e4c3
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 1 deletion.
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
Expand Up @@ -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) { }
Expand Down
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
23 changes: 22 additions & 1 deletion src/core/Akka.Tests/Util/ByteStringSpec.cs
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
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

0 comments on commit 965e4c3

Please sign in to comment.