diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs b/src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs index 91c86cc..2156249 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; } } @@ -302,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); } @@ -331,13 +330,11 @@ 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) await writer.WriteLineAsync(line).ConfigureAwait(false); - - await writer.FlushAsync().ConfigureAwait(false); } ///