private static async Task WriteToFileAsync(string path, FileMode mode, string?contents, Encoding encoding, CancellationToken cancellationToken) { ReadOnlyMemory <byte> preamble = encoding.GetPreamble(); int preambleSize = preamble.Length; using SafeFileHandle fileHandle = OpenHandle(path, mode, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous, GetPreallocationSize(mode, contents, encoding, preambleSize)); long fileOffset = mode == FileMode.Append && fileHandle.CanSeek ? RandomAccess.GetLength(fileHandle) : 0; if (string.IsNullOrEmpty(contents)) { if (preambleSize > 0 && // even if the content is empty, we want to store the preamble fileOffset == 0) // if we're appending to a file that already has data, don't write the preamble. { await RandomAccess.WriteAtOffsetAsync(fileHandle, preamble, fileOffset, cancellationToken).ConfigureAwait(false); } return; } byte[] bytes = ArrayPool <byte> .Shared.Rent(preambleSize + encoding.GetMaxByteCount(Math.Min(contents.Length, ChunkSize))); try { if (fileOffset == 0) { preamble.CopyTo(bytes); } else { preambleSize = 0; // don't append preamble to a non-empty file } Encoder encoder = encoding.GetEncoder(); ReadOnlyMemory <char> remaining = contents.AsMemory(); while (!remaining.IsEmpty) { ReadOnlyMemory <char> toEncode = remaining.Slice(0, Math.Min(remaining.Length, ChunkSize)); remaining = remaining.Slice(toEncode.Length); int encoded = encoder.GetBytes(toEncode.Span, bytes.AsSpan(preambleSize), flush: remaining.IsEmpty); ReadOnlyMemory <byte> toStore = new ReadOnlyMemory <byte>(bytes, 0, preambleSize + encoded); await RandomAccess.WriteAtOffsetAsync(fileHandle, toStore, fileOffset, cancellationToken).ConfigureAwait(false); fileOffset += toStore.Length; preambleSize = 0; } } finally { ArrayPool <byte> .Shared.Return(bytes); } }
static async Task Core(string path, byte[] bytes, CancellationToken cancellationToken) { using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous); await RandomAccess.WriteAtOffsetAsync(sfh, bytes, 0, cancellationToken).ConfigureAwait(false); }