Skip to content

Commit 5d6d3cc

Browse files
v1.0.8: ValueTasks
1 parent ae2dcba commit 5d6d3cc

24 files changed

+354
-356
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4+
## [1.0.8] - 2024-11-17
5+
6+
### Changed
7+
- Use ValueTasks instead of Tasks for improved performance.
8+
49
## [1.0.7] - 2024-03-19
510

611
### Changed

README.md

+18-18
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Log.Logger = log;
5959

6060
Create functions that implement your desired behavior on getting a signal.
6161
```csharp
62-
private async Task WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
62+
private async ValueTask WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
6363
{
6464
try
6565
{
@@ -71,19 +71,19 @@ private async Task WriteToOutputStreamAsync(byte[] arg, StreamWriter writer)
7171
}
7272
}
7373

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

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

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

83-
public Task SignalPromptAsync() =>
83+
public ValueTask SignalPromptAsync() =>
8484
Task.Run(() => _Logger.LogDebug("Prompt"));
8585

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

@@ -123,7 +123,7 @@ public class KestrelMockServer : ConnectionHandler
123123
_Logger = logger;
124124
}
125125

126-
private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
126+
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
127127
{
128128
try
129129
{
@@ -135,28 +135,28 @@ public class KestrelMockServer : ConnectionHandler
135135
}
136136
}
137137

138-
public Task SignalGMCPAsync((string module, string writeback) val)
138+
public ValueTask SignalGMCPAsync((string module, string writeback) val)
139139
{
140140
_Logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
141-
return Task.CompletedTask;
141+
return ValueTask.CompletedTask;
142142
}
143143

144-
public Task SignalMSSPAsync(MSSPConfig val)
144+
public ValueTask SignalMSSPAsync(MSSPConfig val)
145145
{
146146
_Logger.LogDebug("New MSSP: {@MSSPConfig}", val);
147-
return Task.CompletedTask;
147+
return ValueTask.CompletedTask;
148148
}
149149

150-
public Task SignalNAWSAsync(int height, int width)
150+
public ValueTask SignalNAWSAsync(int height, int width)
151151
{
152152
_Logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
153-
return Task.CompletedTask;
153+
return ValueTask.CompletedTask;
154154
}
155155

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

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

169-
private async Task MSDPUpdateBehavior(string resetVariable)
169+
private async ValueTask MSDPUpdateBehavior(string resetVariable)
170170
{
171171
_Logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
172-
await Task.CompletedTask;
172+
await ValueTask.CompletedTask;
173173
}
174174

175-
public async override Task OnConnectedAsync(ConnectionContext connection)
175+
public async override ValueTask OnConnectedAsync(ConnectionContext connection)
176176
{
177177
using (_Logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
178178
{

TelnetNegotiationCore.TestClient/MockPipelineClient.cs

+64-66
Original file line numberDiff line numberDiff line change
@@ -4,93 +4,91 @@
44
using Pipelines.Sockets.Unofficial;
55
using System.Text;
66
using TelnetNegotiationCore.Models;
7-
using System.Collections.Immutable;
87
using Microsoft.Extensions.Logging;
98

10-
namespace TelnetNegotiationCore.TestClient
9+
namespace TelnetNegotiationCore.TestClient;
10+
11+
public class MockPipelineClient(ILogger<MockPipelineClient> logger)
1112
{
12-
public class MockPipelineClient(ILogger<MockPipelineClient> logger)
13+
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
1314
{
14-
15-
private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
15+
try
1616
{
17-
try
18-
{
19-
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
20-
}
21-
catch (ObjectDisposedException ode)
22-
{
23-
logger.LogError(ode, "Stream has been closed");
24-
}
17+
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
2518
}
26-
27-
public static Task WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter t) =>
28-
Task.Run(() => Console.WriteLine(encoding.GetString(writeback.AsSpan())));
29-
30-
public Task SignalGMCPAsync((string module, string writeback) val)
19+
catch (ObjectDisposedException ode)
3120
{
32-
logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
33-
return Task.CompletedTask;
21+
logger.LogError(ode, "Stream has been closed");
3422
}
23+
}
3524

36-
public Task SignalMSSPAsync(MSSPConfig val)
37-
{
38-
logger.LogDebug("New MSSP: {@MSSPConfig}", val);
39-
return Task.CompletedTask;
40-
}
25+
public static async ValueTask WriteBackAsync(byte[] writeback, Encoding encoding, TelnetInterpreter t) =>
26+
await Task.Run(() => Console.WriteLine(encoding.GetString(writeback.AsSpan())));
27+
28+
public ValueTask SignalGMCPAsync((string module, string writeback) val)
29+
{
30+
logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
31+
return ValueTask.CompletedTask;
32+
}
4133

42-
public Task SignalPromptAsync() =>
43-
Task.Run(() => logger.LogDebug("Prompt"));
34+
public ValueTask SignalMSSPAsync(MSSPConfig val)
35+
{
36+
logger.LogDebug("New MSSP: {@MSSPConfig}", val);
37+
return ValueTask.CompletedTask;
38+
}
4439

45-
public async Task StartAsync(string address, int port)
40+
public async ValueTask SignalPromptAsync() =>
41+
await Task.Run(() => logger.LogDebug("Prompt"));
42+
43+
public async Task StartAsync(string address, int port)
44+
{
45+
var client = new TcpClient(address, port);
46+
var stream = client.GetStream();
47+
var pipe = StreamConnection.GetDuplex(stream, new PipeOptions());
48+
49+
var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Client, logger)
4650
{
47-
var client = new TcpClient(address, port);
48-
var stream = client.GetStream();
49-
var pipe = StreamConnection.GetDuplex(stream, new PipeOptions());
51+
CallbackOnSubmitAsync = WriteBackAsync,
52+
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, pipe.Output),
53+
SignalOnGMCPAsync = SignalGMCPAsync,
54+
SignalOnMSSPAsync = SignalMSSPAsync,
55+
SignalOnPromptingAsync = SignalPromptAsync,
56+
CharsetOrder = [Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1")]
57+
}.BuildAsync();
5058

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

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

63-
while (true)
65+
if (telnet != null)
6466
{
65-
string read = Console.ReadLine() ?? string.Empty;
66-
67-
if (telnet != null)
68-
{
69-
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
70-
}
67+
await telnet.SendPromptAsync(telnet?.CurrentEncoding.GetBytes(read));
7168
}
7269
}
70+
}
7371

74-
/// <summary>
75-
/// Read data coming from the server and interpret it.
76-
/// </summary>
77-
/// <param name="reader">The Pipeline Reader</param>
78-
/// <returns>A ValueTask</returns>
79-
static async ValueTask ReadFromPipeline(TelnetInterpreter telnet, PipeReader reader)
72+
/// <summary>
73+
/// Read data coming from the server and interpret it.
74+
/// </summary>
75+
/// <param name="telnet">Interpreter</param>
76+
/// <param name="reader">The Pipeline Reader</param>
77+
/// <returns>A ValueTask</returns>
78+
static async ValueTask ReadFromPipeline(TelnetInterpreter telnet, PipeReader reader)
79+
{
80+
while (true)
8081
{
81-
while (true)
82-
{
83-
// await some data being available
84-
ReadResult read = await reader.ReadAtLeastAsync(1);
82+
// await some data being available
83+
var read = await reader.ReadAtLeastAsync(1);
8584

86-
foreach(var segment in read.Buffer)
87-
{
88-
await telnet.InterpretByteArrayAsync(segment.Span.ToImmutableArray());
89-
}
90-
91-
// tell the pipe that we used everything
92-
reader.AdvanceTo(read.Buffer.End);
85+
foreach(var segment in read.Buffer)
86+
{
87+
await telnet.InterpretByteArrayAsync([..segment.Span]);
9388
}
89+
90+
// tell the pipe that we used everything
91+
reader.AdvanceTo(read.Buffer.End);
9492
}
9593
}
96-
}
94+
}

TelnetNegotiationCore.TestServer/KestrelMockServer.cs

+26-26
Original file line numberDiff line numberDiff line change
@@ -15,48 +15,48 @@ namespace TelnetNegotiationCore.TestServer
1515
{
1616
public class KestrelMockServer : ConnectionHandler
1717
{
18-
private readonly ILogger _Logger;
18+
private readonly ILogger _logger;
1919

20-
public KestrelMockServer(ILogger<KestrelMockServer> logger) : base()
20+
public KestrelMockServer(ILogger<KestrelMockServer> logger)
2121
{
2222
Console.OutputEncoding = Encoding.UTF8;
23-
_Logger = logger;
23+
_logger = logger;
2424
}
2525

26-
private async Task WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
26+
private async ValueTask WriteToOutputStreamAsync(byte[] arg, PipeWriter writer)
2727
{
2828
try
2929
{
3030
await writer.WriteAsync(new ReadOnlyMemory<byte>(arg), CancellationToken.None);
3131
}
3232
catch (ObjectDisposedException ode)
3333
{
34-
_Logger.LogError(ode, "Stream has been closed");
34+
_logger.LogError(ode, "Stream has been closed");
3535
}
3636
}
3737

38-
public Task SignalGMCPAsync((string module, string writeback) val)
38+
public ValueTask SignalGMCPAsync((string module, string writeback) val)
3939
{
40-
_Logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
41-
return Task.CompletedTask;
40+
_logger.LogDebug("GMCP Signal: {Module}: {WriteBack}", val.module, val.writeback);
41+
return ValueTask.CompletedTask;
4242
}
4343

44-
public Task SignalMSSPAsync(MSSPConfig val)
44+
public ValueTask SignalMSSPAsync(MSSPConfig val)
4545
{
46-
_Logger.LogDebug("New MSSP: {@MSSPConfig}", val);
47-
return Task.CompletedTask;
46+
_logger.LogDebug("New MSSP: {@MSSPConfig}", val);
47+
return ValueTask.CompletedTask;
4848
}
4949

50-
public Task SignalNAWSAsync(int height, int width)
50+
public ValueTask SignalNAWSAsync(int height, int width)
5151
{
52-
_Logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
53-
return Task.CompletedTask;
52+
_logger.LogDebug("Client Height and Width updated: {Height}x{Width}", height, width);
53+
return ValueTask.CompletedTask;
5454
}
5555

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

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

69-
private async Task MSDPUpdateBehavior(string resetVariable)
69+
private async ValueTask MSDPUpdateBehavior(string resetVariable)
7070
{
71-
_Logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
72-
await Task.CompletedTask;
71+
_logger.LogDebug("MSDP Reset Request: {@Reset}", resetVariable);
72+
await ValueTask.CompletedTask;
7373
}
7474

75-
public async override Task OnConnectedAsync(ConnectionContext connection)
75+
public override async Task OnConnectedAsync(ConnectionContext connection)
7676
{
77-
using (_Logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
77+
using (_logger.BeginScope(new Dictionary<string, object> { { "ConnectionId", connection.ConnectionId } }))
7878
{
79-
_Logger.LogInformation("{ConnectionId} connected", connection.ConnectionId);
79+
_logger.LogInformation("{ConnectionId} connected", connection.ConnectionId);
8080

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

89-
var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Server, _Logger)
89+
var telnet = await new TelnetInterpreter(TelnetInterpreter.TelnetMode.Server, _logger)
9090
{
9191
CallbackOnSubmitAsync = WriteBackAsync,
9292
SignalOnGMCPAsync = SignalGMCPAsync,
9393
SignalOnMSSPAsync = SignalMSSPAsync,
9494
SignalOnNAWSAsync = SignalNAWSAsync,
9595
SignalOnMSDPAsync = (telnet, config) => SignalMSDPAsync(MSDPHandler, telnet, config),
96-
CallbackNegotiationAsync = (x) => WriteToOutputStreamAsync(x, connection.Transport.Output),
97-
CharsetOrder = new[] { Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1") }
96+
CallbackNegotiationAsync = x => WriteToOutputStreamAsync(x, connection.Transport.Output),
97+
CharsetOrder = [Encoding.GetEncoding("utf-8"), Encoding.GetEncoding("iso-8859-1")]
9898
}
9999
.RegisterMSSPConfig(() => new MSSPConfig
100100
{
@@ -126,7 +126,7 @@ public async override Task OnConnectedAsync(ConnectionContext connection)
126126

127127
connection.Transport.Input.AdvanceTo(buffer.End);
128128
}
129-
_Logger.LogInformation("{ConnectionId} disconnected", connection.ConnectionId);
129+
_logger.LogInformation("{ConnectionId} disconnected", connection.ConnectionId);
130130
}
131131
}
132132
}

0 commit comments

Comments
 (0)