Skip to content

Commit

Permalink
v1.0.8: ValueTasks
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryCordewener committed Nov 18, 2024
1 parent ae2dcba commit 5d6d3cc
Show file tree
Hide file tree
Showing 24 changed files with 354 additions and 356 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Change Log
All notable changes to this project will be documented in this file.

## [1.0.8] - 2024-11-17

### Changed
- Use ValueTasks instead of Tasks for improved performance.

## [1.0.7] - 2024-03-19

### Changed
Expand Down
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Log.Logger = log;

Create functions that implement your desired behavior on getting a signal.
```csharp
private async Task WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
private async ValueTask WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
{
try
{
Expand All @@ -71,19 +71,19 @@ private async Task WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
}
}

public static Task WriteBackAsync(byte[] writeback, Encoding encoding) =>
public static ValueTask WriteBackAsync(byte[] writeback, Encoding encoding) =>
Task.Run(() => Console.WriteLine(encoding.GetString(writeback)));

public Task SignalGMCPAsync((string module, string writeback) val, Encoding encoding) =>
public ValueTask SignalGMCPAsync((string module, string writeback) val, Encoding encoding) =>
Task.Run(() => _Logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback));

public Task SignalMSSPAsync(MSSPConfig val) =>
public ValueTask SignalMSSPAsync(MSSPConfig val) =>
Task.Run(() => _Logger.LogDebug("New MSSP: {@MSSP}", val));

public Task SignalPromptAsync() =>
public ValueTask SignalPromptAsync() =>
Task.Run(() => _Logger.LogDebug("Prompt"));

public Task SignalNAWSAsync(int height, int width) =>
public ValueTask SignalNAWSAsync(int height, int width) =>
Task.Run(() => _Logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width));
```

Expand Down Expand Up @@ -123,7 +123,7 @@ public class KestrelMockServer : ConnectionHandler
_Logger = logger;
}

private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
{
try
{
Expand All @@ -135,28 +135,28 @@ public class KestrelMockServer : ConnectionHandler
}
}

public Task SignalGMCPAsync((string module, string writeback) val)
public ValueTask SignalGMCPAsync((string module, string writeback) val)
{
_Logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
return Task.CompletedTask;
return ValueTask.CompletedTask;
}

public Task SignalMSSPAsync(MSSPConfig val)
public ValueTask SignalMSSPAsync(MSSPConfig val)
{
_Logger.LogDebug("New MSSP: {@MSSPConfig}", val);
return Task.CompletedTask;
return ValueTask.CompletedTask;
}

public Task SignalNAWSAsync(int height, int width)
public ValueTask SignalNAWSAsync(int height, int width)
{
_Logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
return Task.CompletedTask;
return ValueTask.CompletedTask;
}

private static async Task SignalMSDPAsync(MSDPServerHandler handler, TelnetInterpreter telnet, string config) =>
private static async ValueTask SignalMSDPAsync(MSDPServerHandler handler, TelnetInterpreter telnet, string config) =>
await handler.HandleAsync(telnet, config);

public static async Task WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter telnet)
public static async ValueTask WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter telnet)
{
var str = encoding.GetString(writeback);
if (str.StartsWith("echo"))
Expand All @@ -166,13 +166,13 @@ public class KestrelMockServer : ConnectionHandler
Console.WriteLine(encoding.GetString(writeback));
}

private async Task MSDPUpdateBehavior(string resetVariable)
private async ValueTask MSDPUpdateBehavior(string resetVariable)
{
_Logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
await Task.CompletedTask;
await ValueTask.CompletedTask;
}

public async override Task OnConnectedAsync(ConnectionContext connection)
public async override ValueTask OnConnectedAsync(ConnectionContext connection)
{
using (_Logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
{
Expand Down
130 changes: 64 additions & 66 deletions TelnetNegotiationCore.TestClient/MockPipelineClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,93 +4,91 @@
using Pipelines.Sockets.Unofficial;
using System.Text;
using TelnetNegotiationCore.Models;
using System.Collections.Immutable;
using Microsoft.Extensions.Logging;

namespace TelnetNegotiationCore.TestClient
namespace TelnetNegotiationCore.TestClient;

public class MockPipelineClient(ILogger<MockPipelineClient> logger)
{
public class MockPipelineClient(ILogger<MockPipelineClient> logger)
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
{

private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
try
{
try
{
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
}
catch (ObjectDisposedException ode)
{
logger.LogError(ode, "Stream has been closed");
}
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
}

public static Task WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter t) =>
Task.Run(() => Console.WriteLine(encoding.GetString(writeback.AsSpan())));

public Task SignalGMCPAsync((string module, string writeback) val)
catch (ObjectDisposedException ode)
{
logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
return Task.CompletedTask;
logger.LogError(ode, "Stream has been closed");
}
}

public Task SignalMSSPAsync(MSSPConfig val)
{
logger.LogDebug("New MSSP: {@MSSPConfig}", val);
return Task.CompletedTask;
}
public static async ValueTask WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter t) =>
await Task.Run(() => Console.WriteLine(encoding.GetString(writeback.AsSpan())));

public ValueTask SignalGMCPAsync((string module, string writeback) val)
{
logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
return ValueTask.CompletedTask;
}

public Task SignalPromptAsync() =>
Task.Run(() => logger.LogDebug("Prompt"));
public ValueTask SignalMSSPAsync(MSSPConfig val)
{
logger.LogDebug("New MSSP: {@MSSPConfig}", val);
return ValueTask.CompletedTask;
}

public async Task StartAsync(string address, int port)
public async ValueTask SignalPromptAsync() =>
await Task.Run(() => logger.LogDebug("Prompt"));

public async Task StartAsync(string address, int port)
{
var client = new TcpClient(address, port);
var stream = client.GetStream();
var pipe = StreamConnection.GetDuplex(stream, new PipeOptions());

var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Client, logger)
{
var client = new TcpClient(address, port);
var stream = client.GetStream();
var pipe = StreamConnection.GetDuplex(stream, new PipeOptions());
CallbackOnSubmitAsync = WriteBackAsync,
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, pipe.Output),
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnPromptingAsync = SignalPromptAsync,
CharsetOrder = [Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1")]
}.BuildAsync();

var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Client, logger)
{
CallbackOnSubmitAsync = WriteBackAsync,
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, pipe.Output),
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnPromptingAsync = SignalPromptAsync,
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
}.BuildAsync();
var backgroundTask = Task.Run(() => ReadFromPipeline(telnet, pipe.Input));

var backgroundTask = Task.Run(() => ReadFromPipeline(telnet, pipe.Input));
while (true)
{
string read = Console.ReadLine() ?? string.Empty;

while (true)
if (telnet != null)
{
string read = Console.ReadLine() ?? string.Empty;

if (telnet != null)
{
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
}
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
}
}
}

/// <summary>
/// Read data coming from the server and interpret it.
/// </summary>
/// <param name="reader">The Pipeline Reader</param>
/// <returns>A ValueTask</returns>
static async ValueTask ReadFromPipeline(TelnetInterpreter telnet, PipeReader reader)
/// <summary>
/// Read data coming from the server and interpret it.
/// </summary>
/// <param name="telnet">Interpreter</param>
/// <param name="reader">The Pipeline Reader</param>
/// <returns>A ValueTask</returns>
static async ValueTask ReadFromPipeline(TelnetInterpreter telnet, PipeReader reader)
{
while (true)
{
while (true)
{
// await some data being available
ReadResult read = await reader.ReadAtLeastAsync(1);
// await some data being available
var read = await reader.ReadAtLeastAsync(1);

foreach(var segment in read.Buffer)
{
await telnet.InterpretByteArrayAsync(segment.Span.ToImmutableArray());
}

// tell the pipe that we used everything
reader.AdvanceTo(read.Buffer.End);
foreach(var segment in read.Buffer)
{
await telnet.InterpretByteArrayAsync([..segment.Span]);
}

// tell the pipe that we used everything
reader.AdvanceTo(read.Buffer.End);
}
}
}
}
52 changes: 26 additions & 26 deletions TelnetNegotiationCore.TestServer/KestrelMockServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,48 @@ namespace TelnetNegotiationCore.TestServer
{
public class KestrelMockServer : ConnectionHandler
{
private readonly ILogger _Logger;
private readonly ILogger _logger;

public KestrelMockServer(ILogger<KestrelMockServer> logger) : base()
public KestrelMockServer(ILogger<KestrelMockServer> logger)
{
Console.OutputEncoding = Encoding.UTF8;
_Logger = logger;
_logger = logger;
}

private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
{
try
{
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
}
catch (ObjectDisposedException ode)
{
_Logger.LogError(ode, "Stream has been closed");
_logger.LogError(ode, "Stream has been closed");
}
}

public Task SignalGMCPAsync((string module, string writeback) val)
public ValueTask SignalGMCPAsync((string module, string writeback) val)
{
_Logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
return Task.CompletedTask;
_logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
return ValueTask.CompletedTask;
}

public Task SignalMSSPAsync(MSSPConfig val)
public ValueTask SignalMSSPAsync(MSSPConfig val)
{
_Logger.LogDebug("New MSSP: {@MSSPConfig}", val);
return Task.CompletedTask;
_logger.LogDebug("New MSSP: {@MSSPConfig}", val);
return ValueTask.CompletedTask;
}

public Task SignalNAWSAsync(int height, int width)
public ValueTask SignalNAWSAsync(int height, int width)
{
_Logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
return Task.CompletedTask;
_logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
return ValueTask.CompletedTask;
}

private static async Task SignalMSDPAsync(MSDPServerHandler handler, TelnetInterpreter telnet, string config) =>
private static async ValueTask SignalMSDPAsync(MSDPServerHandler handler, TelnetInterpreter telnet, string config) =>
await handler.HandleAsync(telnet, config);

public static async Task WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter telnet)
public static async ValueTask WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter telnet)
{
var str = encoding.GetString(writeback);
if (str.StartsWith("echo"))
Expand All @@ -66,17 +66,17 @@ public static async Task WriteBackAsync(byte[] writeback, Encoding encoding, Tel
Console.WriteLine(encoding.GetString(writeback));
}

private async Task MSDPUpdateBehavior(string resetVariable)
private async ValueTask MSDPUpdateBehavior(string resetVariable)
{
_Logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
await Task.CompletedTask;
_logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
await ValueTask.CompletedTask;
}

public async override Task OnConnectedAsync(ConnectionContext connection)
public override async Task OnConnectedAsync(ConnectionContext connection)
{
using (_Logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
using (_logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
{
_Logger.LogInformation("{ConnectionId} connected", connection.ConnectionId);
_logger.LogInformation("{ConnectionId} connected", connection.ConnectionId);

var MSDPHandler = new MSDPServerHandler(new MSDPServerModel(MSDPUpdateBehavior)
{
Expand All @@ -86,15 +86,15 @@ public async override Task OnConnectedAsync(ConnectionContext connection)
Sendable_Variables = () => ["ROOM"],
});

var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Server, _Logger)
var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Server, _logger)
{
CallbackOnSubmitAsync = WriteBackAsync,
SignalOnGMCPAsync = SignalGMCPAsync,
SignalOnMSSPAsync = SignalMSSPAsync,
SignalOnNAWSAsync = SignalNAWSAsync,
SignalOnMSDPAsync = (telnet, config) => SignalMSDPAsync(MSDPHandler, telnet, config),
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, connection.Transport.Output),
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
CallbackNegotiationAsync = x => WriteToOutputStreamAsync(x, connection.Transport.Output),
CharsetOrder = [Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1")]
}
.RegisterMSSPConfig(() => new MSSPConfig
{
Expand Down Expand Up @@ -126,7 +126,7 @@ public async override Task OnConnectedAsync(ConnectionContext connection)

connection.Transport.Input.AdvanceTo(buffer.End);
}
_Logger.LogInformation("{ConnectionId} disconnected", connection.ConnectionId);
_logger.LogInformation("{ConnectionId} disconnected", connection.ConnectionId);
}
}
}
Expand Down
Loading

0 comments on commit 5d6d3cc

Please sign in to comment.