summary refs log tree commit diff
path: root/crypto/src/util/io
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/util/io')
-rw-r--r--crypto/src/util/io/BaseInputStream.cs49
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs29
-rw-r--r--crypto/src/util/io/BufferedFilterStream.cs18
-rw-r--r--crypto/src/util/io/FilterStream.cs16
-rw-r--r--crypto/src/util/io/LimitedInputStream.cs2
-rw-r--r--crypto/src/util/io/PushbackStream.cs33
-rw-r--r--crypto/src/util/io/Streams.cs169
7 files changed, 302 insertions, 14 deletions
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<byte> 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<int> ReadAsync(Memory<byte> 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<byte> buffer) { throw new NotSupportedException(); }
+        // TODO[api] sealed
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> 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<byte> buffer) { throw new NotSupportedException(); }
+        // TODO[api] sealed
+        public override ValueTask<int> ReadAsync(Memory<byte> 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<byte> buffer)
+        {
+            int count = buffer.Length;
+            for (int i = 0; i < count; ++i)
+            {
+                WriteByte(buffer[i]);
+            }
+        }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> 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<int> ReadAsync(Memory<byte> 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<byte> 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<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            if (m_buf != -1)
+            {
+                if (buffer.IsEmpty)
+                    return new ValueTask<int>(0);
+
+                buffer.Span[0] = (byte)m_buf;
+                m_buf = -1;
+                return new ValueTask<int>(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<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)
         {