From b3cc7f21d581c3d4b7be6b1273eb967018c55624 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 18 Jun 2024 14:58:01 -0400 Subject: [PATCH] Use Stream.CopyTo{Async} in NonCryptographicHashAlgorithm.Append{Async} --- .../Hashing/NonCryptographicHashAlgorithm.cs | 102 +++++++++++------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.cs index 9b18216370a11..4448f67927ef8 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -107,21 +108,7 @@ public void Append(Stream stream) throw new ArgumentNullException(nameof(stream)); } - byte[] buffer = ArrayPool.Shared.Rent(4096); - - while (true) - { - int read = stream.Read(buffer, 0, buffer.Length); - - if (read == 0) - { - break; - } - - Append(new ReadOnlySpan(buffer, 0, read)); - } - - ArrayPool.Shared.Return(buffer); + stream.CopyTo(new CopyToDestinationStream(this)); } /// @@ -147,30 +134,12 @@ public Task AppendAsync(Stream stream, CancellationToken cancellationToken = def throw new ArgumentNullException(nameof(stream)); } - return AppendAsyncCore(stream, cancellationToken); - } - - private async Task AppendAsyncCore(Stream stream, CancellationToken cancellationToken) - { - byte[] buffer = ArrayPool.Shared.Rent(4096); - - while (true) - { -#if NET - int read = await stream.ReadAsync(buffer.AsMemory(), cancellationToken).ConfigureAwait(false); -#else - int read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + return stream.CopyToAsync( + new CopyToDestinationStream(this), +#if !NET + 81_920, // default size used by Stream.CopyTo{Async} #endif - - if (read == 0) - { - break; - } - - Append(new ReadOnlySpan(buffer, 0, read)); - } - - ArrayPool.Shared.Return(buffer); + cancellationToken); } /// @@ -346,5 +315,62 @@ public override int GetHashCode() [DoesNotReturn] private protected static void ThrowDestinationTooShort() => throw new ArgumentException(SR.Argument_DestinationTooShort, "destination"); + + /// Stream-derived type used to support copying from a source stream to this instance via CopyTo{Async}. + private sealed class CopyToDestinationStream(NonCryptographicHashAlgorithm hash) : Stream + { + public override bool CanWrite => true; + + public override void Write(byte[] buffer, int offset, int count) => hash.Append(buffer.AsSpan(offset, count)); + + public override void WriteByte(byte value) => + hash.Append( +#if NET + new ReadOnlySpan(in value) +#else + [value] +#endif + ); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + hash.Append(buffer.AsSpan(offset, count)); + return Task.CompletedTask; + } + +#if NET + public override void Write(ReadOnlySpan buffer) => hash.Append(buffer); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + hash.Append(buffer.Span); + return default; + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => + TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) => + TaskToAsyncResult.End(asyncResult); +#endif + + public override void Flush() { } + + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + + public override void SetLength(long value) => throw new NotSupportedException(); + } } }