33// See the LICENSE file in the project root for more information.
44
55using System . Diagnostics ;
6+ using System . Runtime . CompilerServices ;
67using System . Runtime . InteropServices ;
78using System . Text ;
89using System . Threading ;
@@ -44,19 +45,21 @@ public class StreamWriter : TextWriter
4445 // We don't guarantee thread safety on StreamWriter, but we should at
4546 // least prevent users from trying to write anything while an Async
4647 // write from the same thread is in progress.
47- private volatile Task _asyncWriteTask ;
48+ private Task _asyncWriteTask = Task . CompletedTask ;
4849
4950 private void CheckAsyncTaskInProgress ( )
5051 {
5152 // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety.
5253 // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
53-
54- Task t = _asyncWriteTask ;
55-
56- if ( t != null && ! t . IsCompleted )
57- throw new InvalidOperationException ( SR . InvalidOperation_AsyncIOInProgress ) ;
54+ if ( ! _asyncWriteTask . IsCompleted )
55+ {
56+ ThrowAsyncIOInProgress ( ) ;
57+ }
5858 }
5959
60+ private static void ThrowAsyncIOInProgress ( ) =>
61+ throw new InvalidOperationException ( SR . InvalidOperation_AsyncIOInProgress ) ;
62+
6063 // The high level goal is to be tolerant of encoding errors when we read and very strict
6164 // when we write. Hence, default StreamWriter encoding will throw on encoding error.
6265 // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character
@@ -323,14 +326,13 @@ public override void Write(char value)
323326 }
324327 }
325328
329+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
326330 public override void Write ( char [ ] buffer )
327331 {
328- if ( buffer != null )
329- {
330- WriteCore ( buffer , _autoFlush ) ;
331- }
332+ WriteSpan ( buffer , appendNewLine : false ) ;
332333 }
333334
335+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
334336 public override void Write ( char [ ] buffer , int index , int count )
335337 {
336338 if ( buffer == null )
@@ -350,14 +352,15 @@ public override void Write(char[] buffer, int index, int count)
350352 throw new ArgumentException ( SR . Argument_InvalidOffLen ) ;
351353 }
352354
353- WriteCore ( new ReadOnlySpan < char > ( buffer , index , count ) , _autoFlush ) ;
355+ WriteSpan ( buffer . AsSpan ( index , count ) , appendNewLine : false ) ;
354356 }
355357
358+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
356359 public override void Write ( ReadOnlySpan < char > buffer )
357360 {
358361 if ( GetType ( ) == typeof ( StreamWriter ) )
359362 {
360- WriteCore ( buffer , _autoFlush ) ;
363+ WriteSpan ( buffer , appendNewLine : false ) ;
361364 }
362365 else
363366 {
@@ -367,7 +370,8 @@ public override void Write(ReadOnlySpan<char> buffer)
367370 }
368371 }
369372
370- private unsafe void WriteCore ( ReadOnlySpan < char > buffer , bool autoFlush )
373+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
374+ private unsafe void WriteSpan ( ReadOnlySpan < char > buffer , bool appendNewLine )
371375 {
372376 CheckAsyncTaskInProgress ( ) ;
373377
@@ -422,41 +426,47 @@ private unsafe void WriteCore(ReadOnlySpan<char> buffer, bool autoFlush)
422426 }
423427 }
424428
425- if ( autoFlush )
429+ if ( appendNewLine )
430+ {
431+ char [ ] coreNewLine = CoreNewLine ;
432+ for ( int i = 0 ; i < coreNewLine . Length ; i ++ ) // Generally 1 (\n) or 2 (\r\n) iterations
433+ {
434+ if ( _charPos == _charLen )
435+ {
436+ Flush ( false , false ) ;
437+ }
438+
439+ _charBuffer [ _charPos ] = coreNewLine [ i ] ;
440+ _charPos ++ ;
441+ }
442+ }
443+
444+ if ( _autoFlush )
426445 {
427446 Flush ( true , false ) ;
428447 }
429448 }
430449
450+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
431451 public override void Write ( string value )
432452 {
433- if ( value != null )
434- {
435- WriteCore ( value . AsSpan ( ) , _autoFlush ) ;
436- }
453+ WriteSpan ( value , appendNewLine : false ) ;
437454 }
438455
439- //
440- // Optimize the most commonly used WriteLine overload. This optimization is important for System.Console in particular
441- // because of it will make one WriteLine equal to one call to the OS instead of two in the common case.
442- //
456+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
443457 public override void WriteLine ( string value )
444458 {
445459 CheckAsyncTaskInProgress ( ) ;
446- if ( value != null )
447- {
448- WriteCore ( value . AsSpan ( ) , autoFlush : false ) ;
449- }
450- WriteCore ( new ReadOnlySpan < char > ( CoreNewLine ) , autoFlush : true ) ;
460+ WriteSpan ( value , appendNewLine : true ) ;
451461 }
452462
463+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // prevent WriteSpan from bloating call sites
453464 public override void WriteLine ( ReadOnlySpan < char > value )
454465 {
455466 if ( GetType ( ) == typeof ( StreamWriter ) )
456467 {
457468 CheckAsyncTaskInProgress ( ) ;
458- WriteCore ( value , autoFlush : false ) ;
459- WriteCore ( new ReadOnlySpan < char > ( CoreNewLine ) , autoFlush : true ) ;
469+ WriteSpan ( value , appendNewLine : true ) ;
460470 }
461471 else
462472 {
0 commit comments