// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft; namespace MessagePack { #if !SPAN_BUILTIN internal static class StreamPolyfillExtensions { /// /// Reads from the stream into a memory buffer. /// /// The stream to read from. /// The buffer to read directly into. /// The number of bytes actually read. internal static int Read(this Stream stream, Span buffer) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } byte[] array = ArrayPool.Shared.Rent(buffer.Length); try { int bytesRead = stream.Read(array, 0, buffer.Length); new Span(array, 0, bytesRead).CopyTo(buffer); return bytesRead; } finally { ArrayPool.Shared.Return(array); } } /// /// Reads from the stream into a memory buffer. /// /// The stream to read from. /// The buffer to read directly into. /// A cancellation token. /// The number of bytes actually read. /// /// This method shamelessly copied from the .NET Core 2.1 Stream class: https://github.com/dotnet/coreclr/blob/a113b1c803783c9d64f1f0e946ff9a853e3bc140/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L366-L391. /// internal static ValueTask ReadAsync(this Stream stream, Memory buffer, CancellationToken cancellationToken = default) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) { return new ValueTask(stream.ReadAsync(array.Array, array.Offset, array.Count, cancellationToken)); } else { byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer); async ValueTask FinishReadAsync(Task readTask, byte[] localBuffer, Memory localDestination) { try { int result = await readTask.ConfigureAwait(false); new Span(localBuffer, 0, result).CopyTo(localDestination.Span); return result; } finally { ArrayPool.Shared.Return(localBuffer); } } } } /// /// Writes a span to the stream. /// /// The stream to write to. /// The buffer to write. internal static void Write(this Stream stream, ReadOnlySpan buffer) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } var sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { buffer.CopyTo(sharedBuffer); stream.Write(sharedBuffer, 0, buffer.Length); } finally { ArrayPool.Shared.Return(sharedBuffer); } } /// /// Writes a span to the stream. /// /// The stream to write to. /// The buffer to write. /// A cancellation token. internal static async ValueTask WriteAsync(this Stream stream, ReadOnlyMemory buffer, CancellationToken cancellationToken) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } var sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { buffer.CopyTo(sharedBuffer); await stream.WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); } finally { ArrayPool.Shared.Return(sharedBuffer); } } } #endif }