diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-03-09 16:17:26 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-03-09 16:17:26 +0700 |
commit | 95db89f0bcf07e49ed86b235f3953718a50b6f54 (patch) | |
tree | 3354d8f3687f37f14800eb75627bf34630d90fdf /crypto/src/util/io/Streams.cs | |
parent | Fix some warnings (diff) | |
download | BouncyCastle.NET-ed25519-95db89f0bcf07e49ed86b235f3953718a50b6f54.tar.xz |
Refactoring around Stream usage
Diffstat (limited to '')
-rw-r--r-- | crypto/src/util/io/Streams.cs | 169 |
1 files changed, 159 insertions, 10 deletions
diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs index da8f01068..b975d03bd 100644 --- a/crypto/src/util/io/Streams.cs +++ b/crypto/src/util/io/Streams.cs @@ -1,15 +1,78 @@ using System; using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace Org.BouncyCastle.Utilities.IO { - public static class Streams + public static class Streams { - private const int BufferSize = 4096; + private static readonly int MaxStackAlloc = Environment.Is64BitProcess ? 4096 : 1024; - public static void Drain(Stream inStr) + public static int DefaultBufferSize => MaxStackAlloc; + + public static void CopyTo(Stream source, Stream destination) + { + CopyTo(source, destination, DefaultBufferSize); + } + + public static void CopyTo(Stream source, Stream destination, int bufferSize) + { + int bytesRead; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span<byte> buffer = bufferSize <= MaxStackAlloc + ? stackalloc byte[bufferSize] + : new byte[bufferSize]; + while ((bytesRead = source.Read(buffer)) != 0) + { + destination.Write(buffer[..bytesRead]); + } +#else + byte[] buffer = new byte[bufferSize]; + while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0) + { + destination.Write(buffer, 0, bytesRead); + } +#endif + } + + public static Task CopyToAsync(Stream source, Stream destination) + { + return CopyToAsync(source, destination, DefaultBufferSize); + } + + public static Task CopyToAsync(Stream source, Stream destination, int bufferSize) + { + return CopyToAsync(source, destination, bufferSize, CancellationToken.None); + } + + public static Task CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken) + { + return CopyToAsync(source, destination, DefaultBufferSize, cancellationToken); + } + + public static async Task CopyToAsync(Stream source, Stream destination, int bufferSize, + CancellationToken cancellationToken) + { + int bytesRead; + byte[] buffer = new byte[bufferSize]; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + while ((bytesRead = await ReadAsync(source, new Memory<byte>(buffer), cancellationToken).ConfigureAwait(false)) != 0) + { + await WriteAsync(destination, new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); + } +#else + while ((bytesRead = await ReadAsync(source, buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await WriteAsync(destination, buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + } +#endif + } + + public static void Drain(Stream inStr) { - inStr.CopyTo(Stream.Null, BufferSize); + CopyTo(inStr, Stream.Null, DefaultBufferSize); } /// <summary>Write the full contents of inStr to the destination stream outStr.</summary> @@ -18,7 +81,7 @@ namespace Org.BouncyCastle.Utilities.IO /// <exception cref="IOException">In case of IO failure.</exception> public static void PipeAll(Stream inStr, Stream outStr) { - inStr.CopyTo(outStr, BufferSize); + PipeAll(inStr, outStr, DefaultBufferSize); } /// <summary>Write the full contents of inStr to the destination stream outStr.</summary> @@ -28,7 +91,7 @@ namespace Org.BouncyCastle.Utilities.IO /// <exception cref="IOException">In case of IO failure.</exception> public static void PipeAll(Stream inStr, Stream outStr, int bufferSize) { - inStr.CopyTo(outStr, bufferSize); + CopyTo(inStr, outStr, bufferSize); } /// <summary> @@ -48,12 +111,17 @@ namespace Org.BouncyCastle.Utilities.IO /// <exception cref="IOException"></exception> public static long PipeAllLimited(Stream inStr, long limit, Stream outStr) { - var limited = new LimitedInputStream(inStr, limit); - limited.CopyTo(outStr, BufferSize); - return limit - limited.CurrentLimit; + return PipeAllLimited(inStr, limit, outStr, DefaultBufferSize); } - public static byte[] ReadAll(Stream inStr) + public static long PipeAllLimited(Stream inStr, long limit, Stream outStr, int bufferSize) + { + var limited = new LimitedInputStream(inStr, limit); + CopyTo(limited, outStr, bufferSize); + return limit - limited.CurrentLimit; + } + + public static byte[] ReadAll(Stream inStr) { MemoryStream buf = new MemoryStream(); PipeAll(inStr, buf); @@ -72,6 +140,48 @@ namespace Org.BouncyCastle.Utilities.IO return buf.ToArray(); } + public static Task<int> ReadAsync(Stream source, byte[] buffer, int offset, int count) + { + return source.ReadAsync(buffer, offset, count); + } + + public static Task<int> ReadAsync(Stream source, byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + return source.ReadAsync(buffer, offset, count, cancellationToken); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static ValueTask<int> ReadAsync(Stream source, Memory<byte> buffer, + CancellationToken cancellationToken = default) + { + if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array)) + { + return new ValueTask<int>( + ReadAsync(source, array.Array!, array.Offset, array.Count, cancellationToken)); + } + + byte[] sharedBuffer = new byte[buffer.Length]; + var readTask = ReadAsync(source, sharedBuffer, 0, buffer.Length, cancellationToken); + return FinishReadAsync(readTask, sharedBuffer, buffer); + } + + private static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, + Memory<byte> localDestination) + { + try + { + int result = await readTask.ConfigureAwait(false); + new ReadOnlySpan<byte>(localBuffer, 0, result).CopyTo(localDestination.Span); + return result; + } + finally + { + Array.Fill<byte>(localBuffer, 0x00); + } + } +#endif + public static int ReadFully(Stream inStr, byte[] buf) { return ReadFully(inStr, buf, 0, buf.Length); @@ -117,6 +227,45 @@ namespace Org.BouncyCastle.Utilities.IO throw new ArgumentOutOfRangeException("count"); } + public static Task WriteAsync(Stream destination, byte[] buffer, int offset, int count) + { + return destination.WriteAsync(buffer, offset, count); + } + + public static Task WriteAsync(Stream destination, byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + return destination.WriteAsync(buffer, offset, count, cancellationToken); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static ValueTask WriteAsync(Stream destination, ReadOnlyMemory<byte> buffer, + CancellationToken cancellationToken = default) + { + if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array)) + { + return new ValueTask( + WriteAsync(destination, array.Array!, array.Offset, array.Count, cancellationToken)); + } + + byte[] sharedBuffer = buffer.ToArray(); + var writeTask = WriteAsync(destination, sharedBuffer, 0, buffer.Length, cancellationToken); + return new ValueTask(FinishWriteAsync(writeTask, sharedBuffer)); + } + + private static async Task FinishWriteAsync(Task writeTask, byte[] localBuffer) + { + try + { + await writeTask.ConfigureAwait(false); + } + finally + { + Array.Fill<byte>(localBuffer, 0x00); + } + } +#endif + /// <exception cref="IOException"></exception> public static int WriteBufTo(MemoryStream buf, byte[] output, int offset) { |