ZK_Framework/Assets/Plugins/MessagePack/StreamPolyfillExtensions.cs

138 lines
5.0 KiB
C#
Raw Normal View History

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