Skip to content

Commit

Permalink
Move async method to separate type
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Jun 24, 2020
1 parent b762168 commit e463f1e
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 34 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.sln \
csharp/src/Google.Protobuf/ByteArray.cs \
csharp/src/Google.Protobuf/ByteString.cs \
csharp/src/Google.Protobuf/ByteStringAsync.cs \
csharp/src/Google.Protobuf/CodedInputStream.cs \
csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs \
csharp/src/Google.Protobuf/CodedOutputStream.cs \
Expand Down
21 changes: 21 additions & 0 deletions csharp/src/Google.Protobuf.Test/ByteStringTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
using System.Text;
using NUnit.Framework;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
#if !NET35
using System.Threading.Tasks;
#endif
Expand Down Expand Up @@ -156,6 +159,24 @@ public void CopyTo()
CollectionAssert.AreEqual(data, dest);
}

[Test]
public void GetEnumerator()
{
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
ByteString bs = ByteString.CopyFrom(data);

IEnumerator<byte> genericEnumerator = bs.GetEnumerator();
Assert.IsTrue(genericEnumerator.MoveNext());
Assert.AreEqual(0, genericEnumerator.Current);

IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(0, enumerator.Current);

// Call via LINQ
CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
}

[Test]
public void UnsafeFromBytes()
{
Expand Down
38 changes: 4 additions & 34 deletions csharp/src/Google.Protobuf/ByteString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace Google.Protobuf
/// <summary>
/// Immutable array of bytes.
/// </summary>
[SecuritySafeCritical]
public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString>
{
private static readonly ByteString empty;
Expand All @@ -60,7 +61,6 @@ public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString>
private readonly byte[] bytes;
#endif

[SecuritySafeCritical]
static ByteString()
{
empty = new ByteString(new byte[0]);
Expand All @@ -76,7 +76,6 @@ public static class Unsafe
/// Constructs a new ByteString from the given bytes. The bytes are
/// *not* copied, and must not be modified after this constructor is called.
/// </summary>
[SecuritySafeCritical]
public static ByteString FromBytes(ReadOnlyMemory<byte> bytes)
{
return new ByteString(bytes);
Expand All @@ -87,7 +86,6 @@ public static ByteString FromBytes(ReadOnlyMemory<byte> bytes)
/// <summary>
/// Internal use only. Ensure that the provided array is not mutated and belongs to this instance.
/// </summary>
[SecuritySafeCritical]
internal static ByteString AttachBytes(byte[] bytes)
{
return new ByteString(bytes);
Expand All @@ -98,7 +96,6 @@ internal static ByteString AttachBytes(byte[] bytes)
/// Constructs a new ByteString from the given byte array. The array is
/// *not* copied, and must not be modified after this constructor is called.
/// </summary>
[SecuritySafeCritical]
private ByteString(ReadOnlyMemory<byte> bytes)
{
this.bytes = bytes;
Expand Down Expand Up @@ -127,7 +124,6 @@ public static ByteString Empty
/// </summary>
public int Length
{
[SecuritySafeCritical]
get { return bytes.Length; }
}

Expand All @@ -146,7 +142,6 @@ public bool IsEmpty
/// </summary>
public ReadOnlySpan<byte> Span
{
[SecuritySafeCritical]
get
{
return bytes.Span;
Expand All @@ -159,7 +154,6 @@ public ReadOnlySpan<byte> Span
/// </summary>
public ReadOnlyMemory<byte> Memory
{
[SecuritySafeCritical]
get
{
return bytes;
Expand All @@ -172,7 +166,6 @@ public ReadOnlyMemory<byte> Memory
/// </summary>
/// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks>
/// <returns>A byte array with the same data as this <c>ByteString</c>.</returns>
[SecuritySafeCritical]
public byte[] ToByteArray()
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand All @@ -186,7 +179,6 @@ public byte[] ToByteArray()
/// Converts this <see cref="ByteString"/> into a standard base64 representation.
/// </summary>
/// <returns>A base64 representation of this <c>ByteString</c>.</returns>
[SecuritySafeCritical]
public string ToBase64()
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand All @@ -208,7 +200,6 @@ public string ToBase64()
/// <summary>
/// Constructs a <see cref="ByteString" /> from the Base64 Encoded String.
/// </summary>
[SecuritySafeCritical]
public static ByteString FromBase64(string bytes)
{
// By handling the empty string explicitly, we not only optimize but we fix a
Expand Down Expand Up @@ -247,21 +238,10 @@ public static ByteString FromStream(Stream stream)
/// <param name="stream">The stream to copy into a ByteString.</param>
/// <param name="cancellationToken">The cancellation token to use when reading from the stream, if any.</param>
/// <returns>A ByteString with content read from the given stream.</returns>
public async static Task<ByteString> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
public static Task<ByteString> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{
ProtoPreconditions.CheckNotNull(stream, nameof(stream));
int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
var memoryStream = new MemoryStream(capacity);
// We have to specify the buffer size here, as there's no overload accepting the cancellation token
// alone. But it's documented to use 81920 by default if not specified.
await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
#if NETSTANDARD1_1 || NETSTANDARD2_0
byte[] bytes = memoryStream.ToArray();
#else
// Avoid an extra copy if we can.
byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
#endif
return AttachBytes(bytes);
return ByteStringAsync.FromStreamAsyncCore(stream, cancellationToken);
}
#endif

Expand All @@ -272,7 +252,6 @@ public async static Task<ByteString> FromStreamAsync(Stream stream, Cancellation
/// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form
/// which is primarily useful for testing.
/// </summary>
[SecuritySafeCritical]
public static ByteString CopyFrom(params byte[] bytes)
{
return new ByteString((byte[]) bytes.Clone());
Expand All @@ -281,7 +260,6 @@ public static ByteString CopyFrom(params byte[] bytes)
/// <summary>
/// Constructs a <see cref="ByteString" /> from a portion of a byte array.
/// </summary>
[SecuritySafeCritical]
public static ByteString CopyFrom(byte[] bytes, int offset, int count)
{
byte[] portion = new byte[count];
Expand All @@ -295,7 +273,6 @@ public static ByteString CopyFrom(byte[] bytes, int offset, int count)
/// are copied, so further modifications to the span will not
/// be reflected in the returned <see cref="ByteString" />.
/// </summary>
[SecuritySafeCritical]
public static ByteString CopyFrom(ReadOnlySpan<byte> bytes)
{
return new ByteString(bytes.ToArray());
Expand All @@ -306,7 +283,6 @@ public static ByteString CopyFrom(ReadOnlySpan<byte> bytes)
/// Creates a new <see cref="ByteString" /> by encoding the specified text with
/// the given encoding.
/// </summary>
[SecuritySafeCritical]
public static ByteString CopyFrom(string text, Encoding encoding)
{
return new ByteString(encoding.GetBytes(text));
Expand All @@ -326,7 +302,6 @@ public static ByteString CopyFromUtf8(string text)
public byte this[int index]
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
[SecuritySafeCritical]
get { return bytes.Span[index]; }
#else
get { return bytes[index]; }
Expand All @@ -342,7 +317,6 @@ public static ByteString CopyFromUtf8(string text)
/// </remarks>
/// <param name="encoding">The encoding to use to decode the binary data into text.</param>
/// <returns>The result of decoding the binary data with the given decoding.</returns>
[SecuritySafeCritical]
public string ToString(Encoding encoding)
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand Down Expand Up @@ -402,7 +376,6 @@ IEnumerator IEnumerable.GetEnumerator()
/// <summary>
/// Creates a CodedInputStream from this ByteString's data.
/// </summary>
[SecuritySafeCritical]
public CodedInputStream CreateCodedInput()
{
// We trust CodedInputStream not to reveal the provided byte array or modify it
Expand Down Expand Up @@ -430,7 +403,6 @@ public CodedInputStream CreateCodedInput()
/// <param name="lhs">The first byte string to compare.</param>
/// <param name="rhs">The second byte string to compare.</param>
/// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns>
[SecuritySafeCritical]
public static bool operator ==(ByteString lhs, ByteString rhs)
{
if (ReferenceEquals(lhs, rhs))
Expand Down Expand Up @@ -476,6 +448,7 @@ public CodedInputStream CreateCodedInput()
/// </summary>
/// <param name="obj">The object to compare this with.</param>
/// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns>
[SecuritySafeCritical]
public override bool Equals(object obj)
{
return this == (obj as ByteString);
Expand Down Expand Up @@ -516,7 +489,6 @@ public bool Equals(ByteString other)
/// <summary>
/// Used internally by CodedOutputStream to avoid creating a copy for the write
/// </summary>
[SecuritySafeCritical]
internal void WriteRawBytesTo(CodedOutputStream outputStream)
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand All @@ -539,7 +511,6 @@ internal void WriteRawBytesTo(CodedOutputStream outputStream)
/// <summary>
/// Copies the entire byte array to the destination array provided at the offset specified.
/// </summary>
[SecuritySafeCritical]
public void CopyTo(byte[] array, int position)
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand All @@ -552,7 +523,6 @@ public void CopyTo(byte[] array, int position)
/// <summary>
/// Writes the entire byte array to the provided stream
/// </summary>
[SecuritySafeCritical]
public void WriteTo(Stream outputStream)
{
#if GOOGLE_PROTOBUF_SUPPORT_SYSTEM_MEMORY
Expand Down
64 changes: 64 additions & 0 deletions csharp/src/Google.Protobuf/ByteStringAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Google.Protobuf
{
/// <summary>
/// SecuritySafeCritical attribute can not be placed on types with async methods.
/// This class has ByteString's async methods so it can be marked with SecuritySafeCritical.
/// </summary>
internal static class ByteStringAsync
{
#if !NET35
internal static async Task<ByteString> FromStreamAsyncCore(Stream stream, CancellationToken cancellationToken)
{
int capacity = stream.CanSeek ? checked((int)(stream.Length - stream.Position)) : 0;
var memoryStream = new MemoryStream(capacity);
// We have to specify the buffer size here, as there's no overload accepting the cancellation token
// alone. But it's documented to use 81920 by default if not specified.
await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
#if NETSTANDARD1_1 || NETSTANDARD2_0
byte[] bytes = memoryStream.ToArray();
#else
// Avoid an extra copy if we can.
byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
#endif
return ByteString.AttachBytes(bytes);
}
#endif
}
}

0 comments on commit e463f1e

Please sign in to comment.