138 lines
5.0 KiB
C#
138 lines
5.0 KiB
C#
// 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
|
|
{
|
|
/// <summary>
|
|
/// Reads from the stream into a memory buffer.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to read from.</param>
|
|
/// <param name="buffer">The buffer to read directly into.</param>
|
|
/// <returns>The number of bytes actually read.</returns>
|
|
internal static int Read(this Stream stream, Span<byte> buffer)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
byte[] array = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
try
|
|
{
|
|
int bytesRead = stream.Read(array, 0, buffer.Length);
|
|
new Span<byte>(array, 0, bytesRead).CopyTo(buffer);
|
|
return bytesRead;
|
|
}
|
|
finally
|
|
{
|
|
ArrayPool<byte>.Shared.Return(array);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads from the stream into a memory buffer.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to read from.</param>
|
|
/// <param name="buffer">The buffer to read directly into.</param>
|
|
/// <param name="cancellationToken">A cancellation token.</param>
|
|
/// <returns>The number of bytes actually read.</returns>
|
|
/// <devremarks>
|
|
/// 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.
|
|
/// </devremarks>
|
|
internal static ValueTask<int> ReadAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
|
|
{
|
|
return new ValueTask<int>(stream.ReadAsync(array.Array, array.Offset, array.Count, cancellationToken));
|
|
}
|
|
else
|
|
{
|
|
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer);
|
|
|
|
async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
|
|
{
|
|
try
|
|
{
|
|
int result = await readTask.ConfigureAwait(false);
|
|
new Span<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
|
|
return result;
|
|
}
|
|
finally
|
|
{
|
|
ArrayPool<byte>.Shared.Return(localBuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a span to the stream.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to write to.</param>
|
|
/// <param name="buffer">The buffer to write.</param>
|
|
internal static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
var sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
try
|
|
{
|
|
buffer.CopyTo(sharedBuffer);
|
|
stream.Write(sharedBuffer, 0, buffer.Length);
|
|
}
|
|
finally
|
|
{
|
|
ArrayPool<byte>.Shared.Return(sharedBuffer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a span to the stream.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to write to.</param>
|
|
/// <param name="buffer">The buffer to write.</param>
|
|
/// <param name="cancellationToken">A cancellation token.</param>
|
|
internal static async ValueTask WriteAsync(this Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
var sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
|
try
|
|
{
|
|
buffer.CopyTo(sharedBuffer);
|
|
await stream.WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
finally
|
|
{
|
|
ArrayPool<byte>.Shared.Return(sharedBuffer);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|