Skip to content

Commit

Permalink
Fix Console.Write on iOS (#56713)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximLipnin authored Sep 2, 2021
1 parent 9bdd292 commit faafb32
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 5 deletions.
70 changes: 65 additions & 5 deletions src/libraries/System.Console/src/System/ConsolePal.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,82 @@
// 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.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 void WriteOrCache(StringBuilder cache, Span<char> charBuffer)
{
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));
}

return;
}

// no newlines found, add the entire buffer to the cache
cache.Append(charBuffer);

static unsafe void Print(ReadOnlySpan<char> line)
{
Interop.Sys.Log(ptr, buffer.Length);
fixed (char* ptr = line)
{
Interop.Sys.Log((byte*)ptr, line.Length * 2);
}
}
}
}
Expand All @@ -28,9 +88,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();

Expand Down
19 changes: 19 additions & 0 deletions src/libraries/System.Console/tests/ReadAndWrite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,25 @@ public static async Task OutWriteAndWriteLineOverloads()
}
}

[Fact]
[PlatformSpecific(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS)]
public void TestConsoleWrite()
{
Stream s = new MemoryStream();
TextWriter w = new StreamWriter(s);
((StreamWriter)w).AutoFlush = true;
TextReader r = new StreamReader(s);
Console.SetOut(w);

Console.Write("A");
Console.Write("B");
Console.Write("C");

s.Position = 0;
string line = r.ReadToEnd();
Assert.Equal("ABC", line);
}

private static unsafe void ValidateConsoleEncoding(Encoding encoding)
{
Assert.NotNull(encoding);
Expand Down

0 comments on commit faafb32

Please sign in to comment.