From 8df1d26f054b4ee10bb201b83ef11e232f954ca4 Mon Sep 17 00:00:00 2001 From: Carti Date: Tue, 9 Jun 2026 14:02:09 +0200 Subject: [PATCH 1/3] First attempt --- NBitcoin/Utils.cs | 49 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/NBitcoin/Utils.cs b/NBitcoin/Utils.cs index 4bbb1028e..7e90a4b80 100644 --- a/NBitcoin/Utils.cs +++ b/NBitcoin/Utils.cs @@ -302,7 +302,6 @@ public static IEnumerable> Partition(this IEnumerable source, Func } } -#if !NETSTANDARD1X public static int ReadEx(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellation = default(CancellationToken)) { if (stream == null) @@ -316,6 +315,17 @@ public static IEnumerable> Partition(this IEnumerable source, Func if (offset > buffer.Length - count) throw new ArgumentOutOfRangeException("count"); +#if NET8_0_OR_GREATER + try + { + stream.ReadExactlyAsync(buffer, offset, count, cancellation).GetAwaiter().GetResult(); + return count; + } + catch (EndOfStreamException) + { + return 0; + } +#else int totalReadCount = 0; while (totalReadCount < count) @@ -354,45 +364,8 @@ public static IEnumerable> Partition(this IEnumerable source, Func } return totalReadCount; - } -#else - - public static int ReadEx(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellation = default(CancellationToken)) - { - if(stream == null) throw new ArgumentNullException(nameof(stream)); - if(buffer == null) throw new ArgumentNullException(nameof(buffer)); - if(offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException("offset"); - if(count <= 0 || count > buffer.Length) throw new ArgumentOutOfRangeException("count"); //Disallow 0 as a debugging aid. - if(offset > buffer.Length - count) throw new ArgumentOutOfRangeException("count"); - - //IO interruption not supported on these platforms. - - int totalReadCount = 0; -#if !NOSOCKET - var interruptable = stream is NetworkStream && cancellation.CanBeCanceled; #endif - while(totalReadCount < count) - { - cancellation.ThrowIfCancellationRequested(); - int currentReadCount = 0; -#if !NOSOCKET - if(interruptable) - { - currentReadCount = stream.ReadAsync(buffer, offset + totalReadCount, count - totalReadCount, cancellation).GetAwaiter().GetResult(); - } - else -#endif - { - currentReadCount = stream.Read(buffer, offset + totalReadCount, count - totalReadCount); - } - if(currentReadCount == 0) - return 0; - totalReadCount += currentReadCount; - } - - return totalReadCount; } -#endif #if HAS_SPAN public static int ReadEx(this Stream stream, Span buffer, CancellationToken cancellation = default(CancellationToken)) From cedfb2f010ed295ea26e12210896b56d74b9597b Mon Sep 17 00:00:00 2001 From: Carti Date: Tue, 9 Jun 2026 14:02:09 +0200 Subject: [PATCH 2/3] Second attempt --- NBitcoin/Utils.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/NBitcoin/Utils.cs b/NBitcoin/Utils.cs index 7e90a4b80..706b801a3 100644 --- a/NBitcoin/Utils.cs +++ b/NBitcoin/Utils.cs @@ -315,24 +315,15 @@ public static IEnumerable> Partition(this IEnumerable source, Func if (offset > buffer.Length - count) throw new ArgumentOutOfRangeException("count"); -#if NET8_0_OR_GREATER - try - { - stream.ReadExactlyAsync(buffer, offset, count, cancellation).GetAwaiter().GetResult(); - return count; - } - catch (EndOfStreamException) - { - return 0; - } -#else int totalReadCount = 0; while (totalReadCount < count) { - cancellation.ThrowIfCancellationRequested(); - +#if NET8_0_OR_GREATER + int currentReadCount = stream.ReadAsync(buffer, offset + totalReadCount, count - totalReadCount, cancellation).GetAwaiter().GetResult(); +#else int currentReadCount; + cancellation.ThrowIfCancellationRequested(); //Big performance problem with BeginRead for other stream types than NetworkStream. //Only take the slow path if cancellation is possible. @@ -356,6 +347,7 @@ public static IEnumerable> Partition(this IEnumerable source, Func //IO interruption not supported in this path. currentReadCount = stream.Read(buffer, offset + totalReadCount, count - totalReadCount); } +#endif if (currentReadCount == 0) return 0; @@ -364,7 +356,6 @@ public static IEnumerable> Partition(this IEnumerable source, Func } return totalReadCount; -#endif } #if HAS_SPAN From 78ceee16dbd307d64f00404997d4f2e23fe3d1b1 Mon Sep 17 00:00:00 2001 From: Carti Date: Tue, 9 Jun 2026 14:02:09 +0200 Subject: [PATCH 3/3] Remove preprocessor directive because Stream.ReadAsync seems to be supported on all supported platforms https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.readasync?view=net-10.0 --- NBitcoin/Utils.cs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/NBitcoin/Utils.cs b/NBitcoin/Utils.cs index 706b801a3..0b960a8e8 100644 --- a/NBitcoin/Utils.cs +++ b/NBitcoin/Utils.cs @@ -319,35 +319,7 @@ public static IEnumerable> Partition(this IEnumerable source, Func while (totalReadCount < count) { -#if NET8_0_OR_GREATER int currentReadCount = stream.ReadAsync(buffer, offset + totalReadCount, count - totalReadCount, cancellation).GetAwaiter().GetResult(); -#else - int currentReadCount; - cancellation.ThrowIfCancellationRequested(); - - //Big performance problem with BeginRead for other stream types than NetworkStream. - //Only take the slow path if cancellation is possible. - if (stream is NetworkStream && cancellation.CanBeCanceled) - { - var ar = stream.BeginRead(buffer, offset + totalReadCount, count - totalReadCount, null, null); - if (!ar.CompletedSynchronously) - { - WaitHandle.WaitAny(new WaitHandle[] { ar.AsyncWaitHandle, cancellation.WaitHandle }, -1); - } - - //EndRead might block, so we need to test cancellation before calling it. - //This also is a bug because calling EndRead after BeginRead is contractually required. - //A potential fix is to use the ReadAsync API. Another fix is to register a callback with BeginRead that calls EndRead in all cases. - cancellation.ThrowIfCancellationRequested(); - - currentReadCount = stream.EndRead(ar); - } - else - { - //IO interruption not supported in this path. - currentReadCount = stream.Read(buffer, offset + totalReadCount, count - totalReadCount); - } -#endif if (currentReadCount == 0) return 0;