aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/Google.Protobuf/ByteString.cs
diff options
context:
space:
mode:
authorGravatar Jon Skeet <jonskeet@google.com>2017-01-04 16:40:07 +0000
committerGravatar Jon Skeet <skeet@pobox.com>2017-01-10 13:05:27 +0000
commitfb71df9f0b5a8457255e35cd4cd19c46396e97f1 (patch)
tree80f3f0d27f03265e988f047ac3d47d1368e794d2 /csharp/src/Google.Protobuf/ByteString.cs
parente76d91a9d1c0beed0f083740a5c3288d519160fd (diff)
Add ByteString.FromStream and ByteString.FromStreamAsync in C#
Fixes #2088. We now have separate tests for netcoreapp and net45 to test the two branches here. (netstandard10 doesn't have MemoryStream.GetBuffer) Although most of this library doesn't have any async functionality, this feels like a natural place to locally add it.
Diffstat (limited to 'csharp/src/Google.Protobuf/ByteString.cs')
-rw-r--r--csharp/src/Google.Protobuf/ByteString.cs53
1 files changed, 53 insertions, 0 deletions
diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs
index 5c652cc3..9973d211 100644
--- a/csharp/src/Google.Protobuf/ByteString.cs
+++ b/csharp/src/Google.Protobuf/ByteString.cs
@@ -35,6 +35,10 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
+#if !DOTNET35
+using System.Threading;
+using System.Threading.Tasks;
+#endif
namespace Google.Protobuf
{
@@ -142,6 +146,55 @@ namespace Google.Protobuf
}
/// <summary>
+ /// Constructs a <see cref="ByteString"/> from data in the given stream, synchronously.
+ /// </summary>
+ /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position
+ /// at the start of the call.</remarks>
+ /// <param name="stream">The stream to copy into a ByteString.</param>
+ /// <returns>A ByteString with content read from the given stream.</returns>
+ public static ByteString FromStream(Stream stream)
+ {
+ ProtoPreconditions.CheckNotNull(stream, nameof(stream));
+ int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
+ var memoryStream = new MemoryStream(capacity);
+ stream.CopyTo(memoryStream);
+#if NETSTANDARD1_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);
+ }
+
+#if !DOTNET35
+ /// <summary>
+ /// Constructs a <see cref="ByteString"/> from data in the given stream, asynchronously.
+ /// </summary>
+ /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position
+ /// at the start of the call.</remarks>
+ /// <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))
+ {
+ 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_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);
+ }
+#endif
+
+ /// <summary>
/// Constructs a <see cref="ByteString" /> from the given array. The contents
/// are copied, so further modifications to the array will not
/// be reflected in the returned ByteString.