10
10
// On the use of DangerousGetHandle: If the handle has been invalidated, then the API we pass it to will return an error. These
11
11
// handles should not be exposed to recycling attacks (because they are not exposed at all), but if they were, the worse they
12
12
// could do is diddle with the console buffer.
13
- #pragma warning disable 1634 , 1691
14
13
15
14
using System ;
15
+ using System . Buffers ;
16
16
using System . Text ;
17
17
using System . Runtime . InteropServices ;
18
18
using System . Management . Automation ;
@@ -1479,9 +1479,7 @@ private static void WriteConsoleOutputCJK(ConsoleHandle consoleHandle, Coordinat
1479
1479
bSize . X ++ ;
1480
1480
SMALL_RECT wRegion = writeRegion ;
1481
1481
wRegion . Right ++ ;
1482
- // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
1483
- // get the error code.
1484
- #pragma warning disable 56523
1482
+
1485
1483
result = NativeMethods . WriteConsoleOutput (
1486
1484
consoleHandle . DangerousGetHandle ( ) ,
1487
1485
characterBuffer ,
@@ -1491,9 +1489,6 @@ private static void WriteConsoleOutputCJK(ConsoleHandle consoleHandle, Coordinat
1491
1489
}
1492
1490
else
1493
1491
{
1494
- // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
1495
- // get the error code.
1496
- #pragma warning disable 56523
1497
1492
result = NativeMethods . WriteConsoleOutput (
1498
1493
consoleHandle . DangerousGetHandle ( ) ,
1499
1494
characterBuffer ,
@@ -1796,9 +1791,7 @@ private static bool ReadConsoleOutputCJKSmall
1796
1791
readRegion . Top = ( short ) origin . Y ;
1797
1792
readRegion . Right = ( short ) ( origin . X + bufferSize . X - 1 ) ;
1798
1793
readRegion . Bottom = ( short ) ( origin . Y + bufferSize . Y - 1 ) ;
1799
- // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
1800
- // get the error code.
1801
- #pragma warning disable 56523
1794
+
1802
1795
bool result = NativeMethods . ReadConsoleOutput (
1803
1796
consoleHandle . DangerousGetHandle ( ) ,
1804
1797
characterBuffer ,
@@ -2474,9 +2467,6 @@ internal static string GetConsoleWindowTitle()
2474
2467
DWORD result ;
2475
2468
StringBuilder consoleTitle = new StringBuilder ( ( int ) bufferSize ) ;
2476
2469
2477
- // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
2478
- // get the error code.
2479
- #pragma warning disable 56523
2480
2470
result = NativeMethods . GetConsoleTitle ( consoleTitle , bufferSize ) ;
2481
2471
// If the result is zero, it may mean and error but it may also mean
2482
2472
// that the window title has been set to null. Since we can't tell the
@@ -2561,12 +2551,16 @@ internal static void WriteConsole(ConsoleHandle consoleHandle, ReadOnlySpan<char
2561
2551
return ;
2562
2552
}
2563
2553
2564
- // Native WriteConsole doesn't support output buffer longer than 64K.
2565
- // We need to chop the output string if it is too long.
2566
- int cursor = 0 ; // This records the chopping position in output string
2567
- const int MaxBufferSize = 16383 ; // this is 64K/4 - 1 to account for possible width of each character.
2554
+ // Native WriteConsole doesn't support output buffer longer than 64K, so we need to chop the output string if it is too long.
2555
+ // This records the chopping position in output string.
2556
+ int cursor = 0 ;
2557
+ // This is 64K/4 - 1 to account for possible width of each character.
2558
+ const int MaxBufferSize = 16383 ;
2559
+ const int MaxStackAllocSize = 512 ;
2568
2560
ReadOnlySpan < char > outBuffer ;
2569
2561
2562
+ // In case that a new line is required, we try to write out the last chunk and the new-line string together,
2563
+ // to avoid one extra call to 'WriteConsole' just for a new line string.
2570
2564
while ( cursor + MaxBufferSize < output . Length )
2571
2565
{
2572
2566
outBuffer = output . Slice ( cursor , MaxBufferSize ) ;
@@ -2575,19 +2569,37 @@ internal static void WriteConsole(ConsoleHandle consoleHandle, ReadOnlySpan<char
2575
2569
}
2576
2570
2577
2571
outBuffer = output . Slice ( cursor ) ;
2572
+ if ( ! newLine )
2573
+ {
2574
+ WriteConsole ( consoleHandle , outBuffer ) ;
2575
+ return ;
2576
+ }
2577
+
2578
+ char [ ] rentedArray = null ;
2579
+ string lineEnding = Environment . NewLine ;
2580
+ int size = outBuffer . Length + lineEnding . Length ;
2581
+
2582
+ // We expect the 'size' will often be small, and thus optimize that case with 'stackalloc'.
2583
+ Span < char > buffer = size <= MaxStackAllocSize ? stackalloc char [ size ] : default ;
2578
2584
2579
- if ( newLine )
2585
+ try
2580
2586
{
2581
- var endOfLine = Environment . NewLine . AsSpan ( ) ;
2582
- var endOfLineLength = endOfLine . Length ;
2583
- Span < char > outBufferLine = stackalloc char [ outBuffer . Length + endOfLineLength ] ;
2584
- outBuffer . CopyTo ( outBufferLine ) ;
2585
- endOfLine . CopyTo ( outBufferLine . Slice ( outBufferLine . Length - endOfLineLength ) ) ;
2586
- WriteConsole ( consoleHandle , outBufferLine ) ;
2587
+ if ( buffer . IsEmpty )
2588
+ {
2589
+ rentedArray = ArrayPool < char > . Shared . Rent ( size ) ;
2590
+ buffer = rentedArray . AsSpan ( ) . Slice ( 0 , size ) ;
2591
+ }
2592
+
2593
+ outBuffer . CopyTo ( buffer ) ;
2594
+ lineEnding . CopyTo ( buffer . Slice ( outBuffer . Length ) ) ;
2595
+ WriteConsole ( consoleHandle , buffer ) ;
2587
2596
}
2588
- else
2597
+ finally
2589
2598
{
2590
- WriteConsole ( consoleHandle , outBuffer ) ;
2599
+ if ( rentedArray is not null )
2600
+ {
2601
+ ArrayPool < char > . Shared . Return ( rentedArray ) ;
2602
+ }
2591
2603
}
2592
2604
}
2593
2605
0 commit comments