From 95db89f0bcf07e49ed86b235f3953718a50b6f54 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 9 Mar 2023 16:17:26 +0700 Subject: Refactoring around Stream usage --- crypto/src/cms/CMSTypedStream.cs | 66 +++-------- crypto/src/crypto/io/CipherStream.cs | 44 ++++---- crypto/src/crypto/io/DigestStream.cs | 31 ++++-- crypto/src/crypto/io/MacStream.cs | 31 ++++-- crypto/src/crypto/io/SignerStream.cs | 31 ++++-- crypto/src/openpgp/PgpUtilities.cs | 38 ++----- crypto/src/tls/TlsStream.cs | 26 +++++ crypto/src/util/io/BaseInputStream.cs | 49 +++++++++ crypto/src/util/io/BaseOutputStream.cs | 29 +++++ crypto/src/util/io/BufferedFilterStream.cs | 18 +++ crypto/src/util/io/FilterStream.cs | 16 ++- crypto/src/util/io/LimitedInputStream.cs | 2 +- crypto/src/util/io/PushbackStream.cs | 33 +++++- crypto/src/util/io/Streams.cs | 169 +++++++++++++++++++++++++++-- 14 files changed, 445 insertions(+), 138 deletions(-) create mode 100644 crypto/src/util/io/BufferedFilterStream.cs diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs index 624833848..4cd8014aa 100644 --- a/crypto/src/cms/CMSTypedStream.cs +++ b/crypto/src/cms/CMSTypedStream.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using Org.BouncyCastle.Asn1.Pkcs; @@ -6,69 +5,34 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Cms { - public class CmsTypedStream + public class CmsTypedStream { - private const int BufferSize = 32 * 1024; + private readonly string m_oid; + private readonly Stream m_in; - private readonly string _oid; - private readonly Stream _in; - - public CmsTypedStream( - Stream inStream) - : this(PkcsObjectIdentifiers.Data.Id, inStream, BufferSize) + public CmsTypedStream(Stream inStream) + : this(PkcsObjectIdentifiers.Data.Id, inStream) { } - public CmsTypedStream( - string oid, - Stream inStream) - : this(oid, inStream, BufferSize) + public CmsTypedStream(string oid, Stream inStream) + : this(oid, inStream, Streams.DefaultBufferSize) { } - public CmsTypedStream( - string oid, - Stream inStream, - int bufSize) + public CmsTypedStream(string oid, Stream inStream, int bufSize) { - _oid = oid; - _in = new FullReaderStream(new BufferedStream(inStream, bufSize)); - } + m_oid = oid; + m_in = new BufferedFilterStream(inStream, bufSize); + } - public string ContentType - { - get { return _oid; } - } + public string ContentType => m_oid; - public Stream ContentStream - { - get { return _in; } - } + public Stream ContentStream => m_in; public void Drain() { - Streams.Drain(_in); - _in.Dispose(); + using (m_in) Streams.Drain(m_in); } - - private class FullReaderStream : FilterStream - { - internal FullReaderStream(Stream input) - : base(input) - { - } - - public override int Read(byte[] buf, int off, int len) - { - return Streams.ReadFully(s, buf, off, len); - } - -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public override int Read(Span buffer) - { - return Streams.ReadFully(s, buffer); - } -#endif - } - } + } } diff --git a/crypto/src/crypto/io/CipherStream.cs b/crypto/src/crypto/io/CipherStream.cs index fb821999f..5367640c6 100644 --- a/crypto/src/crypto/io/CipherStream.cs +++ b/crypto/src/crypto/io/CipherStream.cs @@ -1,9 +1,8 @@ using System; -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER -using System.Buffers; -#endif using System.Diagnostics; using System.IO; +using System.Threading; +using System.Threading.Tasks; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.IO; @@ -58,17 +57,15 @@ namespace Org.BouncyCastle.Crypto.IO #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void CopyTo(Stream destination, int bufferSize) { - if (m_readCipher == null) - { - m_stream.CopyTo(destination, bufferSize); - } - else - { - base.CopyTo(destination, bufferSize); - } + Streams.CopyTo(ReadSource, destination, bufferSize); } #endif + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken); + } + public override void Flush() { m_stream.Flush(); @@ -137,6 +134,11 @@ namespace Org.BouncyCastle.Crypto.IO return num; } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(ReadSource, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -181,11 +183,7 @@ namespace Org.BouncyCastle.Crypto.IO byte[] output = null; if (outputSize > 0) { -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - output = ArrayPool.Shared.Rent(outputSize); -#else output = new byte[outputSize]; -#endif } try @@ -200,11 +198,7 @@ namespace Org.BouncyCastle.Crypto.IO { if (output != null) { -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - ArrayPool.Shared.Return(output, clearArray: true); -#else Array.Clear(output, 0, output.Length); -#endif } } } @@ -226,7 +220,7 @@ namespace Org.BouncyCastle.Crypto.IO byte[] output = null; if (outputSize > 0) { - output = ArrayPool.Shared.Rent(outputSize); + output = new byte[outputSize]; } try @@ -241,10 +235,15 @@ namespace Org.BouncyCastle.Crypto.IO { if (output != null) { - ArrayPool.Shared.Return(output, clearArray: true); + Array.Clear(output, 0, output.Length); } } } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(WriteDestination, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) @@ -337,5 +336,8 @@ namespace Org.BouncyCastle.Crypto.IO return bytes; } + + private Stream ReadSource => m_readCipher == null ? m_stream : this; + private Stream WriteDestination => m_writeCipher == null ? m_stream : this; } } diff --git a/crypto/src/crypto/io/DigestStream.cs b/crypto/src/crypto/io/DigestStream.cs index e57a2f851..56e674389 100644 --- a/crypto/src/crypto/io/DigestStream.cs +++ b/crypto/src/crypto/io/DigestStream.cs @@ -1,5 +1,9 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.IO { @@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void CopyTo(Stream destination, int bufferSize) { - if (m_readDigest == null) - { - m_stream.CopyTo(destination, bufferSize); - } - else - { - base.CopyTo(destination, bufferSize); - } + Streams.CopyTo(ReadSource, destination, bufferSize); } #endif + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken); + } + public override void Flush() { m_stream.Flush(); @@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO return n; } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(ReadSource, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO m_writeDigest.BlockUpdate(buffer); } } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(WriteDestination, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) @@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO } base.Dispose(disposing); } + + private Stream ReadSource => m_readDigest == null ? m_stream : this; + private Stream WriteDestination => m_writeDigest == null ? m_stream : this; } } diff --git a/crypto/src/crypto/io/MacStream.cs b/crypto/src/crypto/io/MacStream.cs index bf144bf82..49e207186 100644 --- a/crypto/src/crypto/io/MacStream.cs +++ b/crypto/src/crypto/io/MacStream.cs @@ -1,5 +1,9 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.IO { @@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void CopyTo(Stream destination, int bufferSize) { - if (m_readMac == null) - { - m_stream.CopyTo(destination, bufferSize); - } - else - { - base.CopyTo(destination, bufferSize); - } + Streams.CopyTo(ReadSource, destination, bufferSize); } #endif + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken); + } + public override void Flush() { m_stream.Flush(); @@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO return n; } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(ReadSource, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO m_writeMac.BlockUpdate(buffer); } } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(WriteDestination, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) @@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO } base.Dispose(disposing); } + + private Stream ReadSource => m_readMac == null ? m_stream : this; + private Stream WriteDestination => m_writeMac == null ? m_stream : this; } } diff --git a/crypto/src/crypto/io/SignerStream.cs b/crypto/src/crypto/io/SignerStream.cs index e527e5450..78fa69363 100644 --- a/crypto/src/crypto/io/SignerStream.cs +++ b/crypto/src/crypto/io/SignerStream.cs @@ -1,5 +1,9 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.IO { @@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void CopyTo(Stream destination, int bufferSize) { - if (m_readSigner == null) - { - m_stream.CopyTo(destination, bufferSize); - } - else - { - base.CopyTo(destination, bufferSize); - } + Streams.CopyTo(ReadSource, destination, bufferSize); } #endif + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken); + } + public override void Flush() { m_stream.Flush(); @@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO return n; } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(ReadSource, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO m_writeSigner.BlockUpdate(buffer); } } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(WriteDestination, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) @@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO } base.Dispose(disposing); } + + private Stream ReadSource => m_readSigner == null ? m_stream : this; + private Stream WriteDestination => m_writeSigner == null ? m_stream : this; } } diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs index 03bc73a21..17e100bff 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs @@ -1,7 +1,4 @@ using System; -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER -using System.Buffers; -#endif using System.Collections.Generic; using System.IO; using System.Text; @@ -17,6 +14,7 @@ using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg.OpenPgp { @@ -414,7 +412,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); using (var pOut = lData.Open(output, fileType, file.Name, file.Length, file.LastWriteTime)) { - PipeFileContents(file, pOut, 32768); + PipeFileContents(file, pOut); } } @@ -428,32 +426,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize) - { -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - byte[] buf = ArrayPool.Shared.Rent(bufSize); -#else - byte[] buf = new byte[bufSize]; -#endif + private static void PipeFileContents(FileInfo file, Stream pOut) + { + PipeFileContents(file, pOut, Streams.DefaultBufferSize); + } - try - { - using (var fileStream = file.OpenRead()) - { - int len; - while ((len = fileStream.Read(buf, 0, buf.Length)) > 0) - { - pOut.Write(buf, 0, len); - } - } - } - finally + private static void PipeFileContents(FileInfo file, Stream pOut, int bufferSize) + { + using (var fileStream = file.OpenRead()) { -#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER - ArrayPool.Shared.Return(buf, clearArray: true); -#else - Array.Clear(buf, 0, buf.Length); -#endif + Streams.CopyTo(fileStream, pOut, bufferSize); } } diff --git a/crypto/src/tls/TlsStream.cs b/crypto/src/tls/TlsStream.cs index 5c07da2bf..d7d79a6fa 100644 --- a/crypto/src/tls/TlsStream.cs +++ b/crypto/src/tls/TlsStream.cs @@ -1,5 +1,9 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Tls { @@ -28,6 +32,18 @@ namespace Org.BouncyCastle.Tls get { return true; } } +#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void CopyTo(Stream destination, int bufferSize) + { + Streams.CopyTo(this, destination, bufferSize); + } +#endif + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(this, destination, bufferSize, cancellationToken); + } + protected override void Dispose(bool disposing) { if (disposing) @@ -63,6 +79,11 @@ namespace Org.BouncyCastle.Tls { return m_handler.ReadApplicationData(buffer); } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(this, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -92,6 +113,11 @@ namespace Org.BouncyCastle.Tls { m_handler.WriteApplicationData(buffer); } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(this, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) diff --git a/crypto/src/util/io/BaseInputStream.cs b/crypto/src/util/io/BaseInputStream.cs index eaaf9556d..8f5265fee 100644 --- a/crypto/src/util/io/BaseInputStream.cs +++ b/crypto/src/util/io/BaseInputStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Org.BouncyCastle.Utilities.IO { @@ -10,6 +12,18 @@ namespace Org.BouncyCastle.Utilities.IO public sealed override bool CanSeek { get { return false; } } public sealed override bool CanWrite { get { return false; } } +#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void CopyTo(Stream destination, int bufferSize) + { + Streams.CopyTo(this, destination, bufferSize); + } +#endif + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(this, destination, bufferSize, cancellationToken); + } + public sealed override void Flush() {} public sealed override long Length { get { throw new NotSupportedException(); } } public sealed override long Position @@ -42,12 +56,47 @@ namespace Org.BouncyCastle.Utilities.IO return pos; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override int Read(Span buffer) + { + int count = buffer.Length, pos = 0; + try + { + while (pos < count) + { + int b = ReadByte(); + if (b < 0) + break; + + buffer[pos++] = (byte)b; + } + } + catch (IOException) + { + if (pos == 0) + throw; + } + return pos; + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(this, buffer, cancellationToken); + } +#endif + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public sealed override void SetLength(long value) { throw new NotSupportedException(); } public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + // TODO[api] sealed public override void Write(ReadOnlySpan buffer) { throw new NotSupportedException(); } + // TODO[api] sealed + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } #endif } } diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs index 0fc8e9681..87a850c35 100644 --- a/crypto/src/util/io/BaseOutputStream.cs +++ b/crypto/src/util/io/BaseOutputStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Org.BouncyCastle.Utilities.IO { @@ -11,8 +13,14 @@ namespace Org.BouncyCastle.Utilities.IO public sealed override bool CanWrite { get { return true; } } #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + // TODO[api] sealed public override void CopyTo(Stream destination, int bufferSize) { throw new NotSupportedException(); } #endif + // TODO[api] sealed + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + throw new NotSupportedException(); + } public override void Flush() {} public sealed override long Length { get { throw new NotSupportedException(); } } public sealed override long Position @@ -23,6 +31,11 @@ namespace Org.BouncyCastle.Utilities.IO public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public sealed override int Read(Span buffer) { throw new NotSupportedException(); } + // TODO[api] sealed + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } #endif public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public sealed override void SetLength(long value) { throw new NotSupportedException(); } @@ -37,6 +50,22 @@ namespace Org.BouncyCastle.Utilities.IO } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void Write(ReadOnlySpan buffer) + { + int count = buffer.Length; + for (int i = 0; i < count; ++i) + { + WriteByte(buffer[i]); + } + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(this, buffer, cancellationToken); + } +#endif + public virtual void Write(params byte[] buffer) { Write(buffer, 0, buffer.Length); diff --git a/crypto/src/util/io/BufferedFilterStream.cs b/crypto/src/util/io/BufferedFilterStream.cs new file mode 100644 index 000000000..3c2a4856c --- /dev/null +++ b/crypto/src/util/io/BufferedFilterStream.cs @@ -0,0 +1,18 @@ +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + internal sealed class BufferedFilterStream + : FilterStream + { + internal BufferedFilterStream(Stream s) + : this(s, Streams.DefaultBufferSize) + { + } + + internal BufferedFilterStream(Stream s, int bufferSize) + : base(new BufferedStream(s, bufferSize)) + { + } + } +} diff --git a/crypto/src/util/io/FilterStream.cs b/crypto/src/util/io/FilterStream.cs index 38077edd2..630bdc22f 100644 --- a/crypto/src/util/io/FilterStream.cs +++ b/crypto/src/util/io/FilterStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Org.BouncyCastle.Utilities.IO { @@ -27,9 +29,13 @@ namespace Org.BouncyCastle.Utilities.IO #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void CopyTo(Stream destination, int bufferSize) { - s.CopyTo(destination, bufferSize); + Streams.CopyTo(s, destination, bufferSize); } #endif + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return Streams.CopyToAsync(s, destination, bufferSize, cancellationToken); + } public override void Flush() { s.Flush(); @@ -52,6 +58,10 @@ namespace Org.BouncyCastle.Utilities.IO { return s.Read(buffer); } + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return Streams.ReadAsync(s, buffer, cancellationToken); + } #endif public override int ReadByte() { @@ -74,6 +84,10 @@ namespace Org.BouncyCastle.Utilities.IO { s.Write(buffer); } + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return Streams.WriteAsync(s, buffer, cancellationToken); + } #endif public override void WriteByte(byte value) { diff --git a/crypto/src/util/io/LimitedInputStream.cs b/crypto/src/util/io/LimitedInputStream.cs index 4c6aac631..2f19ef996 100644 --- a/crypto/src/util/io/LimitedInputStream.cs +++ b/crypto/src/util/io/LimitedInputStream.cs @@ -3,7 +3,7 @@ using System.IO; namespace Org.BouncyCastle.Utilities.IO { - internal class LimitedInputStream + internal sealed class LimitedInputStream : BaseInputStream { private readonly Stream m_stream; diff --git a/crypto/src/util/io/PushbackStream.cs b/crypto/src/util/io/PushbackStream.cs index 15ba70501..be6b26d4c 100644 --- a/crypto/src/util/io/PushbackStream.cs +++ b/crypto/src/util/io/PushbackStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Org.BouncyCastle.Utilities.IO { @@ -22,10 +24,22 @@ namespace Org.BouncyCastle.Utilities.IO m_buf = -1; } - s.CopyTo(destination, bufferSize); + Streams.CopyTo(s, destination, bufferSize); } #endif + public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + if (m_buf != -1) + { + byte[] buffer = new byte[1]{ (byte)m_buf }; + await destination.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false); + m_buf = -1; + } + + await Streams.CopyToAsync(s, destination, bufferSize, cancellationToken); + } + public override int Read(byte[] buffer, int offset, int count) { Streams.ValidateBufferArguments(buffer, offset, count); @@ -58,6 +72,21 @@ namespace Org.BouncyCastle.Utilities.IO return s.Read(buffer); } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + if (m_buf != -1) + { + if (buffer.IsEmpty) + return new ValueTask(0); + + buffer.Span[0] = (byte)m_buf; + m_buf = -1; + return new ValueTask(1); + } + + return Streams.ReadAsync(s, buffer, cancellationToken); + } #endif public override int ReadByte() @@ -69,7 +98,7 @@ namespace Org.BouncyCastle.Utilities.IO return tmp; } - return base.ReadByte(); + return s.ReadByte(); } public virtual void Unread(int b) 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 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(buffer), cancellationToken).ConfigureAwait(false)) != 0) + { + await WriteAsync(destination, new ReadOnlyMemory(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); } /// Write the full contents of inStr to the destination stream outStr. @@ -18,7 +81,7 @@ namespace Org.BouncyCastle.Utilities.IO /// In case of IO failure. public static void PipeAll(Stream inStr, Stream outStr) { - inStr.CopyTo(outStr, BufferSize); + PipeAll(inStr, outStr, DefaultBufferSize); } /// Write the full contents of inStr to the destination stream outStr. @@ -28,7 +91,7 @@ namespace Org.BouncyCastle.Utilities.IO /// In case of IO failure. public static void PipeAll(Stream inStr, Stream outStr, int bufferSize) { - inStr.CopyTo(outStr, bufferSize); + CopyTo(inStr, outStr, bufferSize); } /// @@ -48,12 +111,17 @@ namespace Org.BouncyCastle.Utilities.IO /// 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 ReadAsync(Stream source, byte[] buffer, int offset, int count) + { + return source.ReadAsync(buffer, offset, count); + } + + public static Task 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 ReadAsync(Stream source, Memory buffer, + CancellationToken cancellationToken = default) + { + if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) + { + return new ValueTask( + 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 FinishReadAsync(Task readTask, byte[] localBuffer, + Memory localDestination) + { + try + { + int result = await readTask.ConfigureAwait(false); + new ReadOnlySpan(localBuffer, 0, result).CopyTo(localDestination.Span); + return result; + } + finally + { + Array.Fill(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 buffer, + CancellationToken cancellationToken = default) + { + if (MemoryMarshal.TryGetArray(buffer, out ArraySegment 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(localBuffer, 0x00); + } + } +#endif + /// public static int WriteBufTo(MemoryStream buf, byte[] output, int offset) { -- cgit 1.4.1