From 731fb3ab805f7ab21f92416edb4f2a6fbf66ef9f Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 21 Mar 2026 23:45:48 +0500 Subject: [PATCH 1/3] Clean up and simplify VirtualFileExtensions implementation --- .../VirtualFileExtensions.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs index 91c86cc..817188f 100644 --- a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs +++ b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs @@ -8,13 +8,6 @@ namespace Ramstack.FileSystem; /// public static class VirtualFileExtensions { - private static Encoding? s_utf8NoBom; - - /// - /// Gets an instance of the without BOM. - /// - private static Encoding Utf8NoBom => s_utf8NoBom ??= new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - /// /// Asynchronously returns a with /// character encoding that reads from the specified text file. @@ -26,7 +19,7 @@ public static class VirtualFileExtensions /// The result is a that reads from the text file. /// public static ValueTask OpenTextAsync(this VirtualFile file, CancellationToken cancellationToken = default) => - file.OpenTextAsync(Encoding.UTF8, cancellationToken); + file.OpenTextAsync(encoding: null, cancellationToken); /// /// Asynchronously returns a with the specified character encoding @@ -67,7 +60,7 @@ public static ValueTask WriteAsync(this VirtualFile file, Stream stream, Cancell /// containing the full text from the current file. /// public static ValueTask ReadAllTextAsync(this VirtualFile file, CancellationToken cancellationToken = default) => - ReadAllTextAsync(file, Encoding.UTF8, cancellationToken); + ReadAllTextAsync(file, encoding: null, cancellationToken); /// /// Asynchronously reads all the text in the current file with the specified encoding. @@ -81,9 +74,15 @@ public static ValueTask ReadAllTextAsync(this VirtualFile file, Cancella /// public static async ValueTask ReadAllTextAsync(this VirtualFile file, Encoding? encoding, CancellationToken cancellationToken = default) { + // + // Use a manual read loop since .NET 6 lacks a StreamReader.ReadToEndAsync overload that accepts a CancellationToken. + // This ensures the operation remains responsive to cancellation requests. + // + const int BufferSize = 4096; - var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false); + // ReSharper disable once UseAwaitUsing + using var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false); var reader = new StreamReader(stream, encoding ??= Encoding.UTF8); var buffer = (char[]?)null; @@ -217,20 +216,20 @@ static async ValueTask ReadAllBytesUnknownLengthImplAsync(Stream stream, total += count; } - static byte[] ResizeBuffer(byte[] bytes) + static byte[] ResizeBuffer(byte[] oldArray) { - var length = (uint)bytes.Length * 2; + var length = (uint)oldArray.Length * 2; if (length > (uint)Array.MaxLength) - length = (uint)Math.Max(Array.MaxLength, bytes.Length + 1); + length = (uint)Math.Max(Array.MaxLength, oldArray.Length + 1); - var tmp = ArrayPool.Shared.Rent((int)length); - Buffer.BlockCopy(bytes, 0, tmp, 0, bytes.Length); + var newArray = ArrayPool.Shared.Rent((int)length); + oldArray.AsSpan().TryCopyTo(newArray); - var rented = bytes; - bytes = tmp; + var rented = oldArray; + oldArray = newArray; ArrayPool.Shared.Return(rented); - return bytes; + return oldArray; } } From ae66d5c724aa45b169986964d94bde04baf129f9 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 21 Mar 2026 23:53:00 +0500 Subject: [PATCH 2/3] Ensure proper stream disposal in write operations --- src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs index 817188f..208b135 100644 --- a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs +++ b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs @@ -301,7 +301,7 @@ public static ValueTask WriteAllTextAsync(this VirtualFile file, ReadOnlyMemory< /// public static async ValueTask WriteAllTextAsync(this VirtualFile file, ReadOnlyMemory contents, Encoding? encoding, CancellationToken cancellationToken = default) { - var stream = await file.OpenWriteAsync(cancellationToken).ConfigureAwait(false); + await using var stream = await file.OpenWriteAsync(cancellationToken).ConfigureAwait(false); await using var writer = new StreamWriter(stream, encoding!); await writer.WriteAsync(contents, cancellationToken).ConfigureAwait(false); } @@ -330,7 +330,7 @@ public static ValueTask WriteAllLinesAsync(this VirtualFile file, IEnumerable public static async ValueTask WriteAllLinesAsync(this VirtualFile file, IEnumerable contents, Encoding? encoding, CancellationToken cancellationToken = default) { - var stream = await file.OpenWriteAsync(cancellationToken).ConfigureAwait(false); + await using var stream = await file.OpenWriteAsync(cancellationToken).ConfigureAwait(false); await using var writer = new StreamWriter(stream, encoding, bufferSize: -1, leaveOpen: false); foreach (var line in contents) From 17765f73eecda269f1039cc010e2ba5b5ed94807 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 21 Mar 2026 23:56:00 +0500 Subject: [PATCH 3/3] Remove redundant FlushAsync call in WriteAllLinesAsync method --- src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs index 208b135..2156249 100644 --- a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs +++ b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs @@ -335,8 +335,6 @@ public static async ValueTask WriteAllLinesAsync(this VirtualFile file, IEnumera foreach (var line in contents) await writer.WriteLineAsync(line).ConfigureAwait(false); - - await writer.FlushAsync().ConfigureAwait(false); } ///