Skip to content

Commit

Permalink
Use Stream.CopyTo{Async} in NonCryptographicHashAlgorithm.Append{Asyn…
Browse files Browse the repository at this point in the history
…c} (#103669)
  • Loading branch information
stephentoub authored Jun 19, 2024
1 parent 24b59d8 commit f8e575c
Showing 1 changed file with 64 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -107,21 +108,7 @@ public void Append(Stream stream)
throw new ArgumentNullException(nameof(stream));
}

byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);

while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);

if (read == 0)
{
break;
}

Append(new ReadOnlySpan<byte>(buffer, 0, read));
}

ArrayPool<byte>.Shared.Return(buffer);
stream.CopyTo(new CopyToDestinationStream(this));
}

/// <summary>
Expand All @@ -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<byte>.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<byte>(buffer, 0, read));
}

ArrayPool<byte>.Shared.Return(buffer);
cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -346,5 +315,62 @@ public override int GetHashCode()
[DoesNotReturn]
private protected static void ThrowDestinationTooShort() =>
throw new ArgumentException(SR.Argument_DestinationTooShort, "destination");

/// <summary>Stream-derived type used to support copying from a source stream to this instance via CopyTo{Async}.</summary>
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<byte>(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<byte> buffer) => hash.Append(buffer);

public override ValueTask WriteAsync(ReadOnlyMemory<byte> 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();
}
}
}

0 comments on commit f8e575c

Please sign in to comment.