// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace System.IO { // This abstract base class represents a writer that can write a sequential // stream of characters. A subclass must minimally implement the // Write(char) method. // // This class is intended for character output, not bytes. // There are methods on the Stream class for writing bytes. public abstract partial class TextWriter : MarshalByRefObject, IDisposable, IAsyncDisposable { public static readonly TextWriter Null = new NullTextWriter(); // We don't want to allocate on every TextWriter creation, so cache the char array. private static readonly char[] s_coreNewLine = Environment.NewLineConst.ToCharArray(); // s_coreNewLine will be ['\r', '\n'] on Windows and ['\n'] on Unix. This is the opposite, lazily created/cached. private static char[]? s_otherCoreNewLine; /// <summary> /// This is the 'NewLine' property expressed as a char[]. /// It is exposed to subclasses as a protected field for read-only /// purposes. You should only modify it by using the 'NewLine' property. /// In particular you should never modify the elements of the array /// as they are shared among many instances of TextWriter. /// </summary> protected char[] CoreNewLine = s_coreNewLine; private string CoreNewLineStr = Environment.NewLineConst; // Can be null - if so, ask for the Thread's CurrentCulture every time. private readonly IFormatProvider? _internalFormatProvider; protected TextWriter() { } protected TextWriter(IFormatProvider? formatProvider) { _internalFormatProvider = formatProvider; } public virtual IFormatProvider FormatProvider => _internalFormatProvider ?? CultureInfo.CurrentCulture; public virtual void Close() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public virtual ValueTask DisposeAsync() { try { Dispose(); return default; } catch (Exception exc) { return ValueTask.FromException(exc); } } // Clears all buffers for this TextWriter and causes any buffered data to be // written to the underlying device. This default method is empty, but // descendant classes can override the method to provide the appropriate // functionality. public virtual void Flush() { } public abstract Encoding Encoding { get; } /// <summary> /// Returns the line terminator string used by this TextWriter. The default line /// terminator string is Environment.NewLine, which is platform specific. /// On Windows this is a carriage return followed by a line feed ("\r\n"). /// On OSX and Linux this is a line feed ("\n"). /// </summary> /// <remarks> /// The line terminator string is written to the text stream whenever one of the /// WriteLine methods are called. In order for text written by /// the TextWriter to be readable by a TextReader, only one of the following line /// terminator strings should be used: "\r", "\n", or "\r\n". /// </remarks> [AllowNull] public virtual string NewLine { get => CoreNewLineStr; set { value ??= Environment.NewLineConst; if (CoreNewLineStr != value) { CoreNewLineStr = value; CoreNewLine = value == Environment.NewLineConst ? s_coreNewLine : // current OS default Environment.NewLineConst == "\r\n" && value == "\n" ? (s_otherCoreNewLine ??= ['\n']) : // other OS default Environment.NewLineConst == "\n" && value == "\r\n" ? (s_otherCoreNewLine ??= ['\r', '\n']) : // other OS default value.ToCharArray(); // unknown } Debug.Assert(CoreNewLineStr == new string(CoreNewLine)); } } // Writes a character to the text stream. This default method is empty, // but descendant classes can override the method to provide the // appropriate functionality. // public virtual void Write(char value) { } /// <summary> /// Writes a rune to the text stream. /// </summary> /// <param name="value">The rune to write to the text stream.</param> public virtual unsafe void Write(Rune value) { // Convert value to span ReadOnlySpan<char> valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]); // Write span Write(valueChars[0]); if (valueChars.Length > 1) { Write(valueChars[1]); } } // Writes a character array to the text stream. This default method calls // Write(char) for each of the characters in the character array. // If the character array is null, nothing is written. // public virtual void Write(char[]? buffer) { if (buffer != null) { Write(buffer, 0, buffer.Length); } } // Writes a range of a character array to the text stream. This method will // write count characters of data into this TextWriter from the // buffer character array starting at position index. // public virtual void Write(char[] buffer, int index, int count) { ArgumentNullException.ThrowIfNull(buffer); ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfNegative(count); if (buffer.Length - index < count) { throw new ArgumentException(SR.Argument_InvalidOffLen); } for (int i = 0; i < count; i++) Write(buffer[index + i]); } // Writes a span of characters to the text stream. // public virtual void Write(ReadOnlySpan<char> buffer) { char[] array = ArrayPool<char>.Shared.Rent(buffer.Length); try { buffer.CopyTo(new Span<char>(array)); Write(array, 0, buffer.Length); } finally { ArrayPool<char>.Shared.Return(array); } } // Writes the text representation of a boolean to the text stream. This // method outputs either bool.TrueString or bool.FalseString. // public virtual void Write(bool value) { Write(value ? "True" : "False"); } // Writes the text representation of an integer to the text stream. The // text representation of the given value is produced by calling the // int.ToString() method. // public virtual void Write(int value) { Write(value.ToString(FormatProvider)); } // Writes the text representation of an integer to the text stream. The // text representation of the given value is produced by calling the // uint.ToString() method. // [CLSCompliant(false)] public virtual void Write(uint value) { Write(value.ToString(FormatProvider)); } // Writes the text representation of a long to the text stream. The // text representation of the given value is produced by calling the // long.ToString() method. // public virtual void Write(long value) { Write(value.ToString(FormatProvider)); } // Writes the text representation of an unsigned long to the text // stream. The text representation of the given value is produced // by calling the ulong.ToString() method. // [CLSCompliant(false)] public virtual void Write(ulong value) { Write(value.ToString(FormatProvider)); } // Writes the text representation of a float to the text stream. The // text representation of the given value is produced by calling the // float.ToString(float) method. // public virtual void Write(float value) { Write(value.ToString(FormatProvider)); } // Writes the text representation of a double to the text stream. The // text representation of the given value is produced by calling the // double.ToString(double) method. // public virtual void Write(double value) { Write(value.ToString(FormatProvider)); } public virtual void Write(decimal value) { Write(value.ToString(FormatProvider)); } // Writes a string to the text stream. If the given string is null, nothing // is written to the text stream. // public virtual void Write(string? value) { if (value != null) { Write(value.ToCharArray()); } } // Writes the text representation of an object to the text stream. If the // given object is null, nothing is written to the text stream. // Otherwise, the object's ToString method is called to produce the // string representation, and the resulting string is then written to the // output stream. // public virtual void Write(object? value) { if (value != null) { if (value is IFormattable f) { Write(f.ToString(null, FormatProvider)); } else Write(value.ToString()); } } /// <summary> /// Equivalent to Write(stringBuilder.ToString()) however it uses the /// StringBuilder.GetChunks() method to avoid creating the intermediate string /// </summary> /// <param name="value">The string (as a StringBuilder) to write to the stream</param> public virtual void Write(StringBuilder? value) { if (value != null) { foreach (ReadOnlyMemory<char> chunk in value.GetChunks()) Write(chunk.Span); } } // Writes out a formatted string. Uses the same semantics as // string.Format. // public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { Write(string.Format(FormatProvider, format, arg0)); } // Writes out a formatted string. Uses the same semantics as // string.Format. // public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { Write(string.Format(FormatProvider, format, arg0, arg1)); } // Writes out a formatted string. Uses the same semantics as // string.Format. // public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { Write(string.Format(FormatProvider, format, arg0, arg1, arg2)); } // Writes out a formatted string. Uses the same semantics as // string.Format. // public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { Write(string.Format(FormatProvider, format, arg)); } /// <summary> /// Writes a formatted string to the text stream, using the same semantics as <see cref="string.Format(string, ReadOnlySpan{object?})"/>. /// </summary> /// <param name="format">A composite format string.</param> /// <param name="arg">An object span that contains zero or more objects to format and write.</param> public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) { Write(string.Format(FormatProvider, format, arg)); } // Writes a line terminator to the text stream. The default line terminator // is Environment.NewLine, but this value can be changed by setting the NewLine property. // public virtual void WriteLine() { Write(CoreNewLine); } // Writes a character followed by a line terminator to the text stream. // public virtual void WriteLine(char value) { Write(value); WriteLine(); } /// <summary> /// Writes a rune followed by a line terminator to the text stream. /// </summary> /// <param name="value">The rune to write to the text stream.</param> public virtual unsafe void WriteLine(Rune value) { // Convert value to span ReadOnlySpan<char> valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]); if (valueChars.Length > 1) { Write(valueChars[0]); WriteLine(valueChars[1]); } else { WriteLine(valueChars[0]); } } // Writes an array of characters followed by a line terminator to the text // stream. // public virtual void WriteLine(char[]? buffer) { Write(buffer); WriteLine(); } // Writes a range of a character array followed by a line terminator to the // text stream. // public virtual void WriteLine(char[] buffer, int index, int count) { Write(buffer, index, count); WriteLine(); } public virtual void WriteLine(ReadOnlySpan<char> buffer) { char[] array = ArrayPool<char>.Shared.Rent(buffer.Length); try { buffer.CopyTo(new Span<char>(array)); WriteLine(array, 0, buffer.Length); } finally { ArrayPool<char>.Shared.Return(array); } } // Writes the text representation of a boolean followed by a line // terminator to the text stream. // public virtual void WriteLine(bool value) { Write(value); WriteLine(); } // Writes the text representation of an integer followed by a line // terminator to the text stream. // public virtual void WriteLine(int value) { Write(value); WriteLine(); } // Writes the text representation of an unsigned integer followed by // a line terminator to the text stream. // [CLSCompliant(false)] public virtual void WriteLine(uint value) { Write(value); WriteLine(); } // Writes the text representation of a long followed by a line terminator // to the text stream. // public virtual void WriteLine(long value) { Write(value); WriteLine(); } // Writes the text representation of an unsigned long followed by // a line terminator to the text stream. // [CLSCompliant(false)] public virtual void WriteLine(ulong value) { Write(value); WriteLine(); } // Writes the text representation of a float followed by a line terminator // to the text stream. // public virtual void WriteLine(float value) { Write(value); WriteLine(); } // Writes the text representation of a double followed by a line terminator // to the text stream. // public virtual void WriteLine(double value) { Write(value); WriteLine(); } public virtual void WriteLine(decimal value) { Write(value); WriteLine(); } // Writes a string followed by a line terminator to the text stream. // public virtual void WriteLine(string? value) { if (value != null) { Write(value); } Write(CoreNewLineStr); } /// <summary> /// Equivalent to WriteLine(stringBuilder.ToString()) however it uses the /// StringBuilder.GetChunks() method to avoid creating the intermediate string /// </summary> public virtual void WriteLine(StringBuilder? value) { Write(value); WriteLine(); } // Writes the text representation of an object followed by a line // terminator to the text stream. // public virtual void WriteLine(object? value) { if (value == null) { WriteLine(); } else { // Call WriteLine(value.ToString), not Write(Object), WriteLine(). // This makes calls to WriteLine(Object) atomic. if (value is IFormattable f) { WriteLine(f.ToString(null, FormatProvider)); } else { WriteLine(value.ToString()); } } } // Writes out a formatted string and a new line. Uses the same // semantics as string.Format. // public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { WriteLine(string.Format(FormatProvider, format, arg0)); } // Writes out a formatted string and a new line. Uses the same // semantics as string.Format. // public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { WriteLine(string.Format(FormatProvider, format, arg0, arg1)); } // Writes out a formatted string and a new line. Uses the same // semantics as string.Format. // public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { WriteLine(string.Format(FormatProvider, format, arg0, arg1, arg2)); } // Writes out a formatted string and a new line. Uses the same // semantics as string.Format. // public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { WriteLine(string.Format(FormatProvider, format, arg)); } /// <summary> /// Writes out a formatted string and a new line to the text stream, using the same semantics as <see cref="string.Format(string, ReadOnlySpan{object?})"/>. /// </summary> /// <param name="format">A composite format string.</param> /// <param name="arg">An object span that contains zero or more objects to format and write.</param> public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) { WriteLine(string.Format(FormatProvider, format, arg)); } #region Task based Async APIs public virtual Task WriteAsync(char value) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char>)state!; t.Item1.Write(t.Item2); }, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); /// <summary> /// Writes a rune to the text stream asynchronously. /// </summary> /// <param name="value">The rune to write to the text stream.</param> /// <returns>A task that represents the asynchronous write operation.</returns> public virtual unsafe Task WriteAsync(Rune value) { ReadOnlySpan<char> valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]); if (valueChars.Length > 1) { return Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char, char>)state!; t.Item1.Write(t.Item2); t.Item1.Write(t.Item3); }, new TupleSlim<TextWriter, char, char>(this, valueChars[0], valueChars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } return WriteAsync(valueChars[0]); } public virtual Task WriteAsync(string? value) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, string?>)state!; t.Item1.Write(t.Item2); }, new TupleSlim<TextWriter, string?>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); /// <summary> /// Writes a string to the text stream asynchronously. /// </summary> /// <param name="value">The string to write. If <paramref name="value"/> is <see langword="null"/>, nothing is written.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param> /// <returns>A task that represents the asynchronous write operation.</returns> public Task WriteAsync(string? value, CancellationToken cancellationToken) => WriteAsync(value.AsMemory(), cancellationToken); /// <summary> /// Equivalent to WriteAsync(stringBuilder.ToString()) however it uses the /// StringBuilder.GetChunks() method to avoid creating the intermediate string /// </summary> /// <param name="value">The string (as a StringBuilder) to write to the stream</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> public virtual Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) { return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : value == null ? Task.CompletedTask : WriteAsyncCore(value, cancellationToken); async Task WriteAsyncCore(StringBuilder sb, CancellationToken ct) { foreach (ReadOnlyMemory<char> chunk in sb.GetChunks()) { await WriteAsync(chunk, ct).ConfigureAwait(false); } } } public Task WriteAsync(char[]? buffer) { if (buffer == null) { return Task.CompletedTask; } return WriteAsync(buffer, 0, buffer.Length); } public virtual Task WriteAsync(char[] buffer, int index, int count) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char[], int, int>)state!; t.Item1.Write(t.Item2, t.Item3, t.Item4); }, new TupleSlim<TextWriter, char[], int, int>(this, buffer, index, count), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) => cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ? WriteAsync(array.Array!, array.Offset, array.Count) : Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, ReadOnlyMemory<char>>)state!; t.Item1.Write(t.Item2.Span); }, new TupleSlim<TextWriter, ReadOnlyMemory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteLineAsync(char value) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char>)state!; t.Item1.WriteLine(t.Item2); }, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); /// <summary> /// Writes a rune followed by a line terminator to the text stream asynchronously. /// </summary> /// <param name="value">The rune to write to the text stream.</param> /// <returns>A task that represents the asynchronous write operation.</returns> public virtual unsafe Task WriteLineAsync(Rune value) { ReadOnlySpan<char> valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]); if (valueChars.Length > 1) { return Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char, char>)state!; t.Item1.Write(t.Item2); t.Item1.WriteLine(t.Item3); }, new TupleSlim<TextWriter, char, char>(this, valueChars[0], valueChars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } return WriteLineAsync(valueChars[0]); } public virtual Task WriteLineAsync(string? value) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, string?>)state!; t.Item1.WriteLine(t.Item2); }, new TupleSlim<TextWriter, string?>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); /// <summary> /// Writes a string followed by a line terminator to the text stream asynchronously. /// </summary> /// <param name="value">The string to write. If <paramref name="value"/> is <see langword="null"/>, only the line terminator is written.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param> /// <returns>A task that represents the asynchronous write operation.</returns> public Task WriteLineAsync(string? value, CancellationToken cancellationToken) => WriteLineAsync(value.AsMemory(), cancellationToken); /// <summary> /// Equivalent to WriteLineAsync(stringBuilder.ToString()) however it uses the /// StringBuilder.GetChunks() method to avoid creating the intermediate string /// </summary> /// <param name="value">The string (as a StringBuilder) to write to the stream</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> public virtual Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) { return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : value == null ? WriteAsync(CoreNewLine, cancellationToken) : WriteLineAsyncCore(value, cancellationToken); async Task WriteLineAsyncCore(StringBuilder sb, CancellationToken ct) { foreach (ReadOnlyMemory<char> chunk in sb.GetChunks()) { await WriteAsync(chunk, ct).ConfigureAwait(false); } await WriteAsync(CoreNewLine, ct).ConfigureAwait(false); } } public Task WriteLineAsync(char[]? buffer) { if (buffer == null) { return WriteLineAsync(); } return WriteLineAsync(buffer, 0, buffer.Length); } public virtual Task WriteLineAsync(char[] buffer, int index, int count) => Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, char[], int, int>)state!; t.Item1.WriteLine(t.Item2, t.Item3, t.Item4); }, new TupleSlim<TextWriter, char[], int, int>(this, buffer, index, count), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) => cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ? WriteLineAsync(array.Array!, array.Offset, array.Count) : Task.Factory.StartNew(static state => { var t = (TupleSlim<TextWriter, ReadOnlyMemory<char>>)state!; t.Item1.WriteLine(t.Item2.Span); }, new TupleSlim<TextWriter, ReadOnlyMemory<char>>(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteLineAsync() { return WriteAsync(CoreNewLine); } /// <summary> /// Writes a line terminator to the text stream asynchronously. /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param> /// <returns>A task that represents the asynchronous write operation.</returns> public Task WriteLineAsync(CancellationToken cancellationToken) => WriteAsync(CoreNewLine, cancellationToken); public virtual Task FlushAsync() { return Task.Factory.StartNew(static state => ((TextWriter)state!).Flush(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } /// <summary> /// Asynchronously clears all buffers for the current writer and causes any buffered data to /// be written to the underlying device. /// </summary> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param> /// <returns>A <see cref="Task"/> that represents the asynchronous flush operation.</returns> /// <exception cref="ObjectDisposedException">The text writer is disposed.</exception> /// <exception cref="InvalidOperationException">The writer is currently in use by a previous write operation.</exception> public virtual Task FlushAsync(CancellationToken cancellationToken) => cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : FlushAsync(); #endregion private sealed class NullTextWriter : TextWriter { internal NullTextWriter() { } public override IFormatProvider FormatProvider => CultureInfo.InvariantCulture; public override Encoding Encoding => Encoding.Unicode; [AllowNull] public override string NewLine { get => base.NewLine; set { } } // To avoid all unnecessary overhead in the base, override all Flush/Write methods as pure nops. public override void Flush() { } public override Task FlushAsync() => Task.CompletedTask; public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; public override void Write(char value) { } public override void Write(Rune value) { } public override void Write(char[]? buffer) { } public override void Write(char[] buffer, int index, int count) { } public override void Write(ReadOnlySpan<char> buffer) { } public override void Write(bool value) { } public override void Write(int value) { } public override void Write(uint value) { } public override void Write(long value) { } public override void Write(ulong value) { } public override void Write(float value) { } public override void Write(double value) { } public override void Write(decimal value) { } public override void Write(string? value) { } public override void Write(object? value) { } public override void Write(StringBuilder? value) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) { } public override Task WriteAsync(char value) => Task.CompletedTask; public override Task WriteAsync(Rune value) => Task.CompletedTask; public override Task WriteAsync(string? value) => Task.CompletedTask; public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; public override Task WriteAsync(char[] buffer, int index, int count) => Task.CompletedTask; public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) => Task.CompletedTask; public override void WriteLine() { } public override void WriteLine(char value) { } public override void WriteLine(Rune value) { } public override void WriteLine(char[]? buffer) { } public override void WriteLine(char[] buffer, int index, int count) { } public override void WriteLine(ReadOnlySpan<char> buffer) { } public override void WriteLine(bool value) { } public override void WriteLine(int value) { } public override void WriteLine(uint value) { } public override void WriteLine(long value) { } public override void WriteLine(ulong value) { } public override void WriteLine(float value) { } public override void WriteLine(double value) { } public override void WriteLine(decimal value) { } public override void WriteLine(string? value) { } public override void WriteLine(StringBuilder? value) { } public override void WriteLine(object? value) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) { } public override Task WriteLineAsync(char value) => Task.CompletedTask; public override Task WriteLineAsync(Rune value) => Task.CompletedTask; public override Task WriteLineAsync(string? value) => Task.CompletedTask; public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; public override Task WriteLineAsync(char[] buffer, int index, int count) => Task.CompletedTask; public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) => Task.CompletedTask; public override Task WriteLineAsync() => Task.CompletedTask; } public static TextWriter Synchronized(TextWriter writer) { ArgumentNullException.ThrowIfNull(writer); return !RuntimeFeature.IsMultithreadingSupported || writer is SyncTextWriter ? writer : new SyncTextWriter(writer); } internal sealed class SyncTextWriter : TextWriter, IDisposable { private readonly TextWriter _out; internal SyncTextWriter(TextWriter t) { _out = t; } public override Encoding Encoding => _out.Encoding; public override IFormatProvider FormatProvider => _out.FormatProvider; [AllowNull] public override string NewLine { [MethodImpl(MethodImplOptions.Synchronized)] get => _out.NewLine; [MethodImpl(MethodImplOptions.Synchronized)] set => _out.NewLine = value; } [MethodImpl(MethodImplOptions.Synchronized)] public override void Close() => _out.Close(); [MethodImpl(MethodImplOptions.Synchronized)] protected override void Dispose(bool disposing) { // Explicitly pick up a potentially methodimpl'ed Dispose if (disposing) ((IDisposable)_out).Dispose(); } [MethodImpl(MethodImplOptions.Synchronized)] public override void Flush() => _out.Flush(); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(char value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(Rune value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(char[]? buffer) => _out.Write(buffer); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(char[] buffer, int index, int count) => _out.Write(buffer, index, count); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(ReadOnlySpan<char> buffer) => _out.Write(buffer); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(bool value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(int value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(uint value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(long value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(ulong value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(float value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(double value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(decimal value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(string? value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(StringBuilder? value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write(object? value) => _out.Write(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) => _out.Write(format, arg0); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) => _out.Write(format, arg0, arg1); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) => _out.Write(format, arg0, arg1, arg2); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object?[] arg) => _out.Write(format, arg); [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) => _out.Write(format, arg); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine() => _out.WriteLine(); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(char value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(Rune value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(decimal value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(char[]? buffer) => _out.WriteLine(buffer); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(char[] buffer, int index, int count) => _out.WriteLine(buffer, index, count); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(ReadOnlySpan<char> buffer) => _out.WriteLine(buffer); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(bool value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(int value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(uint value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(long value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(ulong value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(float value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(double value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(string? value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(StringBuilder? value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine(object? value) => _out.WriteLine(value); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) => _out.WriteLine(format, arg0); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) => _out.WriteLine(format, arg0, arg1); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) => _out.WriteLine(format, arg0, arg1, arg2); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object?[] arg) => _out.WriteLine(format, arg); [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params ReadOnlySpan<object?> arg) => _out.WriteLine(format, arg); // // On SyncTextWriter all APIs should run synchronously, even the async ones. // [MethodImpl(MethodImplOptions.Synchronized)] public override ValueTask DisposeAsync() { Dispose(); return default; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(char value) { Write(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(Rune value) { Write(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(string? value) { Write(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } Write(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(char[] buffer, int index, int count) { Write(buffer, index, count); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } Write(buffer.Span); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } WriteLine(buffer.Span); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(char value) { WriteLine(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(Rune value) { WriteLine(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync() { WriteLine(); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(string? value) { WriteLine(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } WriteLine(value); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task WriteLineAsync(char[] buffer, int index, int count) { WriteLine(buffer, index, count); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task FlushAsync() { Flush(); return Task.CompletedTask; } [MethodImpl(MethodImplOptions.Synchronized)] public override Task FlushAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } Flush(); return Task.CompletedTask; } } } }