diff --git a/CHANGELOG.md b/CHANGELOG.md index c36208dc3..82eb41f30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Release Notes ==== +# 12-30-2024 +DotNext.IO 5.17.1 +* Fixed `EndOfStreamException` caused by async read from `PoolingBufferedStream` + # 12-29-2024 This release is aimed to improve AOT compatibility. All the examples in the repo are now AOT compatible. DotNext 5.17.0 diff --git a/README.md b/README.md index 9cf9fed9b..b74fba780 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ This release is aimed to improve AOT compatibility. All the examples in the repo DotNext.Threading 5.17.0 * Improved AOT support -DotNext.IO 5.17.0 +DotNext.IO 5.17.1 * Reduced memory consumption for applications that use `FileReader` and `FileWriter` classes. These classes are now implemented by using lazy buffer pattern. It means that the different instances can reuse the same buffer taken from the pool * Fixed [255](https://github.com/dotnet/dotNext/issues/255) * `PoolingBufferedStream` is introduced to replace classic [BufferedStream](https://learn.microsoft.com/en-us/dotnet/api/system.io.bufferedstream). This class supports memory pooling and implements lazy buffer pattern diff --git a/src/DotNext.IO/DotNext.IO.csproj b/src/DotNext.IO/DotNext.IO.csproj index 4bda5105b..6d38e5a76 100644 --- a/src/DotNext.IO/DotNext.IO.csproj +++ b/src/DotNext.IO/DotNext.IO.csproj @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 5.17.0 + 5.17.1 DotNext.IO MIT diff --git a/src/DotNext.IO/IO/PoolingBufferedStream.cs b/src/DotNext.IO/IO/PoolingBufferedStream.cs index 4013f348a..97cec0186 100644 --- a/src/DotNext.IO/IO/PoolingBufferedStream.cs +++ b/src/DotNext.IO/IO/PoolingBufferedStream.cs @@ -477,7 +477,7 @@ public override ValueTask ReadAsync(Memory data, CancellationToken to { task = ValueTask.FromException(new NotSupportedException()); } - else if (buffer.IsEmpty) + else if (data.IsEmpty) { task = new(result: 0); } diff --git a/src/DotNext.Tests/IO/PoolingBufferedStreamTests.cs b/src/DotNext.Tests/IO/PoolingBufferedStreamTests.cs index 6747a9b96..331e3d0c5 100644 --- a/src/DotNext.Tests/IO/PoolingBufferedStreamTests.cs +++ b/src/DotNext.Tests/IO/PoolingBufferedStreamTests.cs @@ -341,4 +341,36 @@ public static void ResetBuffer() bufferedStream.Reset(); False(bufferedStream.HasBufferedDataToWrite); } + + [Fact] + public static void ReadFromFile() + { + var expected = RandomBytes(4096); + using var handle = File.OpenHandle(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), FileMode.CreateNew, FileAccess.ReadWrite, + options: FileOptions.DeleteOnClose); + RandomAccess.Write(handle, expected, fileOffset: 0L); + RandomAccess.FlushToDisk(handle); + + using var bufferedStream = new PoolingBufferedStream(handle.AsUnbufferedStream(FileAccess.Read)) { MaxBufferSize = 128 }; + var actual = new byte[expected.Length]; + bufferedStream.ReadExactly(actual); + + Equal(expected, actual); + } + + [Fact] + public static async Task ReadFromFileAsync() + { + var expected = RandomBytes(4096); + using var handle = File.OpenHandle(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), FileMode.CreateNew, FileAccess.ReadWrite, + options: FileOptions.DeleteOnClose | FileOptions.Asynchronous); + await RandomAccess.WriteAsync(handle, expected, fileOffset: 0L); + RandomAccess.FlushToDisk(handle); + + await using var bufferedStream = new PoolingBufferedStream(handle.AsUnbufferedStream(FileAccess.Read)) { MaxBufferSize = 128 }; + var actual = new byte[expected.Length]; + await bufferedStream.ReadExactlyAsync(actual); + + Equal(expected, actual); + } } \ No newline at end of file diff --git a/src/examples/HyParViewPeer/Program.cs b/src/examples/HyParViewPeer/Program.cs index ee214c4bd..612238c73 100644 --- a/src/examples/HyParViewPeer/Program.cs +++ b/src/examples/HyParViewPeer/Program.cs @@ -132,8 +132,8 @@ static Task PrintNeighborsAsync(HttpContext context) file sealed class RumorSender : Disposable, IRumorSender { - internal const string SenderAddressHeader = "X-Sender-Address"; - internal const string SenderIdHeader = "X-Rumor-ID"; + private const string SenderAddressHeader = "X-Sender-Address"; + private const string SenderIdHeader = "X-Rumor-ID"; internal const string RumorResource = "/rumor"; internal const string BroadcastResource = "/broadcast";