File: src\LegacySupport\FilePolyfills\FilePolyfills.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj (Microsoft.Extensions.AI.Abstractions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#if !NET9_0_OR_GREATER
 
#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
 
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.IO;
 
/// <summary>
/// Provides polyfill extension members for <see cref="File"/> for older frameworks.
/// </summary>
[ExcludeFromCodeCoverage]
internal static class FilePolyfills
{
    extension(File)
    {
#if !NET
        /// <summary>
        /// Asynchronously reads all bytes from a file.
        /// </summary>
        /// <param name="path">The file to read from.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous read operation, which wraps the byte array containing the contents of the file.</returns>
        public static async Task<byte[]> ReadAllBytesAsync(string path, CancellationToken cancellationToken = default)
        {
            using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
            byte[] data = new byte[stream.Length];
            int totalRead = 0;
            while (totalRead < data.Length)
            {
                int read = await stream.ReadAsync(data, totalRead, data.Length - totalRead, cancellationToken).ConfigureAwait(false);
                if (read == 0)
                {
                    break;
                }
 
                totalRead += read;
            }
 
            return data;
        }
#endif
 
        /// <summary>
        /// Asynchronously writes all bytes to a file.
        /// </summary>
        /// <param name="path">The file to write to.</param>
        /// <param name="bytes">The bytes to write to the file.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous write operation.</returns>
        public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory<byte> bytes, CancellationToken cancellationToken = default)
        {
            // Try to avoid ToArray() if the data is backed by a byte[] with offset 0 and matching length
            byte[] byteArray;
            if (MemoryMarshal.TryGetArray(bytes, out ArraySegment<byte> segment) &&
                segment.Offset == 0 &&
                segment.Count == segment.Array!.Length)
            {
                byteArray = segment.Array;
            }
            else
            {
                byteArray = bytes.ToArray();
            }
 
#if NET
            await File.WriteAllBytesAsync(path, byteArray, cancellationToken).ConfigureAwait(false);
#else
            using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true);
            await stream.WriteAsync(byteArray, 0, byteArray.Length, cancellationToken).ConfigureAwait(false);
#endif
        }
    }
}
 
#endif