// 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
}