-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Fix Console.Write on iOS #56713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Console.Write on iOS #56713
Changes from 31 commits
71b3888
8f6f7bc
a97f0d5
90bf33c
e6806e4
2bdb6e7
612dc1c
6f8542b
47d8f01
4f0653a
14545a8
725e64c
5b8499c
61bb68b
68e10e7
ac45868
2e57d54
c8aa30f
b76ddfe
b999fff
49f5c41
aaa1f35
90da2a0
88685c7
976954b
245edb3
a234df5
34d215f
1eb7528
a2e118e
732a62f
9375848
7504d30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,83 @@ | ||
// 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.IO; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
namespace System | ||
{ | ||
internal sealed class NSLogStream : ConsoleStream | ||
{ | ||
public NSLogStream() : base(FileAccess.Write) {} | ||
private readonly StringBuilder _buffer = new StringBuilder(); | ||
private readonly Encoding _encoding; | ||
private readonly Decoder _decoder; | ||
|
||
public NSLogStream(Encoding encoding) : base(FileAccess.Write) | ||
{ | ||
_encoding = encoding; | ||
_decoder = _encoding.GetDecoder(); | ||
} | ||
|
||
public override int Read(Span<byte> buffer) => throw Error.GetReadNotSupported(); | ||
|
||
public override unsafe void Write(ReadOnlySpan<byte> buffer) | ||
{ | ||
fixed (byte* ptr = buffer) | ||
int maxCharCount = _encoding.GetMaxCharCount(buffer.Length); | ||
char[]? pooledBuffer = null; | ||
Span<char> charSpan = maxCharCount <= 512 ? stackalloc char[512] : (pooledBuffer = ArrayPool<char>.Shared.Rent(maxCharCount)); | ||
try | ||
{ | ||
int count = _decoder.GetChars(buffer, charSpan, false); | ||
if (count > 0) | ||
{ | ||
WriteOrCache(_buffer, charSpan.Slice(0, count)); | ||
} | ||
} | ||
finally | ||
{ | ||
if (pooledBuffer != null) | ||
{ | ||
ArrayPool<char>.Shared.Return(pooledBuffer); | ||
} | ||
} | ||
} | ||
|
||
private static unsafe void WriteOrCache(StringBuilder cache, Span<char> charBuffer) | ||
MaximLipnin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
{ | ||
int lastNewLine = charBuffer.LastIndexOf('\n'); | ||
if (lastNewLine != -1) | ||
{ | ||
Span<char> lineSpan = charBuffer.Slice(0, lastNewLine); | ||
if (cache.Length > 0) | ||
{ | ||
Print(cache.Append(lineSpan).ToString()); | ||
cache.Clear(); | ||
} | ||
else | ||
{ | ||
Print(lineSpan); | ||
} | ||
|
||
if (lastNewLine + 1 < charBuffer.Length) | ||
{ | ||
cache.Append(charBuffer.Slice(lastNewLine + 1, charBuffer.Length - lastNewLine - 1)); | ||
MaximLipnin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
|
||
return; | ||
} | ||
|
||
// no newlines found, add the entire buffer to the cache | ||
cache.Append(charBuffer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General question about this change... it seems like this change means that if I have code that does: while (true)
{
Console.Write(".");
await Task.Delay(1000);
} to print out a period to the console once a second, for example as you might find with a progress indicator in a console app, that the data will never be emitted to the console, because newlines are never found. Am I understanding correctly? Is that not an issue, e.g. because Console is only used for diagnostic messages in an iOS app? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is a thing I wasn't able to beat. I mentioned it a bit earlier #56713 (comment). The current Xamarin implementation has the same drawback so it won't surprise the existing customers. |
||
|
||
static void Print(ReadOnlySpan<char> line) | ||
{ | ||
Interop.Sys.Log(ptr, buffer.Length); | ||
fixed (char* ptr = line) | ||
{ | ||
Interop.Sys.Log((byte*)ptr, line.Length * 2); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -28,9 +89,9 @@ internal static void EnsureConsoleInitialized() | |
|
||
public static Stream OpenStandardInput() => throw new PlatformNotSupportedException(); | ||
|
||
public static Stream OpenStandardOutput() => new NSLogStream(); | ||
public static Stream OpenStandardOutput() => new NSLogStream(OutputEncoding); | ||
|
||
public static Stream OpenStandardError() => new NSLogStream(); | ||
public static Stream OpenStandardError() => new NSLogStream(OutputEncoding); | ||
|
||
public static Encoding InputEncoding => throw new PlatformNotSupportedException(); | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.