Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Stream.CopyTo{Async} in NonCryptographicHashAlgorithm.Append{Async} #103669

Merged
merged 1 commit into from
Jun 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
}
}
Loading