// 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;
{
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)
+ public override void Write(ReadOnlySpan<byte> 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)
{
- fixed (byte* ptr = buffer)
+ 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);
+ }
}
}
}
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();
}
}
+ [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);