Skip to content

Commit 450b89e

Browse files
committed
Replace PayloadData with ReadOnlySpan<byte>.
This simplifies the deserialization code, which can now potentially be used with Memory<byte> in the future. It also improves performance as ReadOnlySpan<byte> is a smaller type to pass on the stack.
1 parent 36433e8 commit 450b89e

15 files changed

+71
-68
lines changed

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public async Task<ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
4444
var firstByte = payload.HeaderByte;
4545
if (firstByte == OkPayload.Signature)
4646
{
47-
var ok = OkPayload.Create(payload);
47+
var ok = OkPayload.Create(payload.AsSpan());
4848
RecordsAffected = (RecordsAffected ?? 0) + ok.AffectedRowCount;
4949
LastInsertId = unchecked((long) ok.LastInsertId);
5050
if (ok.NewSchema != null)
@@ -61,7 +61,7 @@ public async Task<ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
6161
{
6262
try
6363
{
64-
var localInfile = LocalInfilePayload.Create(payload);
64+
var localInfile = LocalInfilePayload.Create(payload.AsSpan());
6565
if (!IsHostVerified(Connection)
6666
&& !localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal))
6767
throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE");
@@ -89,15 +89,15 @@ public async Task<ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
8989
}
9090
else
9191
{
92-
int ReadColumnCount(ArraySegment<byte> arraySegment)
92+
int ReadColumnCount(ReadOnlySpan<byte> span)
9393
{
94-
var reader = new ByteArrayReader(arraySegment);
94+
var reader = new ByteArrayReader(span);
9595
var columnCount_ = (int) reader.ReadLengthEncodedInteger();
9696
if (reader.BytesRemaining != 0)
9797
throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324");
9898
return columnCount_;
9999
}
100-
var columnCount = ReadColumnCount(payload.ArraySegment);
100+
var columnCount = ReadColumnCount(payload.AsSpan());
101101

102102
// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
103103
Utility.Resize(ref m_columnDefinitionPayloads, columnCount * 96);
@@ -124,7 +124,7 @@ int ReadColumnCount(ArraySegment<byte> arraySegment)
124124
if (!Session.SupportsDeprecateEof)
125125
{
126126
payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
127-
EofPayload.Create(payload);
127+
EofPayload.Create(payload.AsSpan());
128128
}
129129

130130
LastInsertId = -1;
@@ -225,16 +225,17 @@ Row ScanRowAsyncRemainder(PayloadData payload, Row row_)
225225
{
226226
if (payload.HeaderByte == EofPayload.Signature)
227227
{
228-
if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload, Session.SupportsDeprecateEof))
228+
var span = payload.AsSpan();
229+
if (Session.SupportsDeprecateEof && OkPayload.IsOk(span, Session.SupportsDeprecateEof))
229230
{
230-
var ok = OkPayload.Create(payload, Session.SupportsDeprecateEof);
231+
var ok = OkPayload.Create(span, Session.SupportsDeprecateEof);
231232
BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
232233
m_rowBuffered = null;
233234
return null;
234235
}
235236
if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload))
236237
{
237-
var eof = EofPayload.Create(payload);
238+
var eof = EofPayload.Create(span);
238239
BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
239240
m_rowBuffered = null;
240241
return null;

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void FinishQuerying()
185185
var payload = QueryPayload.Create("DO SLEEP(0);");
186186
SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
187187
payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
188-
OkPayload.Create(payload);
188+
OkPayload.Create(payload.AsSpan());
189189
}
190190

191191
lock (m_lock)
@@ -286,7 +286,7 @@ public async Task ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer
286286
m_payloadHandler = new StandardPayloadHandler(byteHandler);
287287

288288
payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
289-
initialHandshake = InitialHandshakePayload.Create(payload);
289+
initialHandshake = InitialHandshakePayload.Create(payload.AsSpan());
290290

291291
// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
292292
string authPluginName;
@@ -361,7 +361,7 @@ public async Task ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer
361361
payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
362362
}
363363

364-
OkPayload.Create(payload);
364+
OkPayload.Create(payload.AsSpan());
365365

366366
if (m_useCompression)
367367
m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
@@ -396,12 +396,12 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavio
396396
Log.Debug("Session{0} ServerVersion={1} supports reset connection; sending reset connection request", m_logArguments);
397397
await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
398398
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
399-
OkPayload.Create(payload);
399+
OkPayload.Create(payload.AsSpan());
400400

401401
// the "reset connection" packet also resets the connection charset, so we need to change that back to our default
402402
await SendAsync(s_setNamesUtf8mb4Payload, ioBehavior, cancellationToken).ConfigureAwait(false);
403403
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
404-
OkPayload.Create(payload);
404+
OkPayload.Create(payload.AsSpan());
405405
}
406406
else
407407
{
@@ -426,7 +426,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavio
426426
Log.Debug("Session{0} optimistic reauthentication failed; logging in again", m_logArguments);
427427
payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
428428
}
429-
OkPayload.Create(payload);
429+
OkPayload.Create(payload.AsSpan());
430430
}
431431

432432
return true;
@@ -446,7 +446,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavio
446446
private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs, PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
447447
{
448448
// if the server didn't support the hashed password; rehash with the new challenge
449-
var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);
449+
var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload.AsSpan());
450450
m_logArguments[1] = switchRequest.Name;
451451
Log.Debug("Session{0} switching to AuthenticationMethod '{1}'", m_logArguments);
452452
switch (switchRequest.Name)
@@ -475,10 +475,10 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
475475
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
476476

477477
// OK payload can be sent immediately (e.g., if password is empty( (short-circuiting the )
478-
if (OkPayload.IsOk(payload, SupportsDeprecateEof))
478+
if (OkPayload.IsOk(payload.AsSpan(), SupportsDeprecateEof))
479479
return payload;
480480

481-
var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload);
481+
var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.AsSpan());
482482
if (cachingSha2ServerResponsePayload.Succeeded)
483483
return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
484484

@@ -585,7 +585,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
585585
var payloadContent = switchRequestName == "caching_sha2_password" ? (byte) 0x02 : (byte) 0x01;
586586
await SendReplyAsync(new PayloadData(new[] { payloadContent }), ioBehavior, cancellationToken).ConfigureAwait(false);
587587
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
588-
var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload);
588+
var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload.AsSpan());
589589
return Encoding.ASCII.GetString(publicKeyPayload.Data);
590590
}
591591

@@ -604,7 +604,7 @@ public async ValueTask<bool> TryPingAsync(IOBehavior ioBehavior, CancellationTok
604604
Log.Debug("Session{0} pinging server", m_logArguments);
605605
await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
606606
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
607-
OkPayload.Create(payload);
607+
OkPayload.Create(payload.AsSpan());
608608
Log.Info("Session{0} successfully pinged server", m_logArguments);
609609
return true;
610610
}
@@ -658,7 +658,7 @@ public ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Cancellat
658658
if (payload.HeaderByte != ErrorPayload.Signature)
659659
return new ValueTask<PayloadData>(payload);
660660

661-
var exception = CreateExceptionForErrorPayload(payload);
661+
var exception = CreateExceptionForErrorPayload(payload.AsSpan());
662662
return ValueTaskExtensions.FromException<PayloadData>(exception);
663663
}
664664

@@ -1140,27 +1140,27 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
11401140
if (!SupportsDeprecateEof)
11411141
{
11421142
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
1143-
EofPayload.Create(payload);
1143+
EofPayload.Create(payload.AsSpan());
11441144
}
11451145

11461146
// first (and only) row
11471147
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
1148-
void ReadRow(ArraySegment<byte> arraySegment, out int? connectionId_, out string serverVersion_)
1148+
void ReadRow(ReadOnlySpan<byte> span, out int? connectionId_, out string serverVersion_)
11491149
{
1150-
var reader = new ByteArrayReader(arraySegment);
1150+
var reader = new ByteArrayReader(span);
11511151
var length = reader.ReadLengthEncodedIntegerOrNull();
11521152
connectionId_ = (length != -1 && Utf8Parser.TryParse(reader.ReadByteString(length), out int id, out _)) ? id : default(int?);
11531153
length = reader.ReadLengthEncodedIntegerOrNull();
11541154
serverVersion_ = length != -1 ? Encoding.UTF8.GetString(reader.ReadByteString(length)) : null;
11551155
}
1156-
ReadRow(payload.ArraySegment, out var connectionId, out var serverVersion);
1156+
ReadRow(payload.AsSpan(), out var connectionId, out var serverVersion);
11571157

11581158
// OK/EOF payload
11591159
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
1160-
if (OkPayload.IsOk(payload, SupportsDeprecateEof))
1161-
OkPayload.Create(payload, SupportsDeprecateEof);
1160+
if (OkPayload.IsOk(payload.AsSpan(), SupportsDeprecateEof))
1161+
OkPayload.Create(payload.AsSpan(), SupportsDeprecateEof);
11621162
else
1163-
EofPayload.Create(payload);
1163+
EofPayload.Create(payload.AsSpan());
11641164

11651165
if (connectionId.HasValue && serverVersion != null)
11661166
{
@@ -1241,7 +1241,7 @@ private PayloadData TryAsyncContinuation(Task<ArraySegment<byte>> task)
12411241
}
12421242
var payload = new PayloadData(bytes);
12431243
if (payload.HeaderByte == ErrorPayload.Signature)
1244-
throw CreateExceptionForErrorPayload(payload);
1244+
throw CreateExceptionForErrorPayload(payload.AsSpan());
12451245
return payload;
12461246
}
12471247

@@ -1337,9 +1337,9 @@ private byte[] CreateConnectionAttributes(string programName)
13371337
}
13381338
}
13391339

1340-
private Exception CreateExceptionForErrorPayload(PayloadData payload)
1340+
private Exception CreateExceptionForErrorPayload(ReadOnlySpan<byte> span)
13411341
{
1342-
var errorPayload = ErrorPayload.Create(payload);
1342+
var errorPayload = ErrorPayload.Create(span);
13431343
var exception = errorPayload.ToException();
13441344
Log.Error(exception, "Session{0} got error payload: Code={1}, State={2}, Message={3}", m_logArguments[0], errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
13451345
return exception;

src/MySqlConnector/MySql.Data.MySqlClient/MySqlCommand.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ private async Task DoPrepareAsync(IOBehavior ioBehavior, CancellationToken cance
131131
{
132132
await Connection.Session.SendAsync(new PayloadData(statement.StatementBytes), ioBehavior, cancellationToken).ConfigureAwait(false);
133133
var payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
134-
var response = StatementPrepareResponsePayload.Create(payload);
134+
var response = StatementPrepareResponsePayload.Create(payload.AsSpan());
135135

136136
ColumnDefinitionPayload[] parameters = null;
137137
if (response.ParameterCount > 0)
@@ -148,7 +148,7 @@ private async Task DoPrepareAsync(IOBehavior ioBehavior, CancellationToken cance
148148
if (!Connection.Session.SupportsDeprecateEof)
149149
{
150150
payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
151-
EofPayload.Create(payload);
151+
EofPayload.Create(payload.AsSpan());
152152
}
153153
}
154154

@@ -167,7 +167,7 @@ private async Task DoPrepareAsync(IOBehavior ioBehavior, CancellationToken cance
167167
if (!Connection.Session.SupportsDeprecateEof)
168168
{
169169
payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
170-
EofPayload.Create(payload);
170+
EofPayload.Create(payload.AsSpan());
171171
}
172172
}
173173

src/MySqlConnector/MySql.Data.MySqlClient/MySqlConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
131131
using (var initDatabasePayload = InitDatabasePayload.Create(databaseName))
132132
await m_session.SendAsync(initDatabasePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
133133
var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
134-
OkPayload.Create(payload);
134+
OkPayload.Create(payload.AsSpan());
135135
m_session.DatabaseOverride = databaseName;
136136
}
137137

src/MySqlConnector/Protocol/PayloadData.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public PayloadData(ArraySegment<byte> data, bool isPooled = false)
1818
}
1919

2020
public ArraySegment<byte> ArraySegment { get; }
21+
public ReadOnlySpan<byte> AsSpan() => ArraySegment.AsSpan();
2122
public byte HeaderByte => ArraySegment.Array[ArraySegment.Offset];
2223

2324
public void Dispose()

src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Text;
23
using MySqlConnector.Protocol.Serialization;
34
using MySqlConnector.Utilities;
@@ -11,13 +12,13 @@ internal sealed class AuthenticationMethodSwitchRequestPayload
1112

1213
public const byte Signature = 0xFE;
1314

14-
public static AuthenticationMethodSwitchRequestPayload Create(PayloadData payload)
15+
public static AuthenticationMethodSwitchRequestPayload Create(ReadOnlySpan<byte> span)
1516
{
16-
var reader = new ByteArrayReader(payload.ArraySegment);
17+
var reader = new ByteArrayReader(span);
1718
reader.ReadByte(Signature);
1819
string name;
1920
byte[] data;
20-
if (payload.ArraySegment.Count == 1)
21+
if (span.Length == 1)
2122
{
2223
// if the packet is just the header byte (0xFE), it's an "Old Authentication Method Switch Request Packet"
2324
// (possibly sent by a server that doesn't support CLIENT_PLUGIN_AUTH)

src/MySqlConnector/Protocol/Payloads/AuthenticationMoreDataPayload.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using MySqlConnector.Protocol.Serialization;
23

34
namespace MySqlConnector.Protocol.Payloads
@@ -8,9 +9,9 @@ internal sealed class AuthenticationMoreDataPayload
89

910
public const byte Signature = 0x01;
1011

11-
public static AuthenticationMoreDataPayload Create(PayloadData payload)
12+
public static AuthenticationMoreDataPayload Create(ReadOnlySpan<byte> span)
1213
{
13-
var reader = new ByteArrayReader(payload.ArraySegment);
14+
var reader = new ByteArrayReader(span);
1415
reader.ReadByte(Signature);
1516
return new AuthenticationMoreDataPayload(reader.ReadByteString(reader.BytesRemaining).ToArray());
1617
}

src/MySqlConnector/Protocol/Payloads/CachingSha2ServerResponsePayload.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using MySqlConnector.Protocol.Serialization;
23

34
namespace MySqlConnector.Protocol.Payloads
@@ -20,9 +21,9 @@ private CachingSha2ServerResponsePayload(bool succeeded, bool fullAuthRequired)
2021

2122
public bool FullAuthRequired { get; }
2223

23-
public static CachingSha2ServerResponsePayload Create(PayloadData payload)
24+
public static CachingSha2ServerResponsePayload Create(ReadOnlySpan<byte> span)
2425
{
25-
var reader = new ByteArrayReader(payload.ArraySegment);
26+
var reader = new ByteArrayReader(span);
2627
reader.ReadByte(Signature);
2728
var secondByte = reader.ReadByte();
2829

src/MySqlConnector/Protocol/Payloads/EofPayload.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ internal sealed class EofPayload
99
public int WarningCount { get; }
1010
public ServerStatus ServerStatus { get; }
1111

12-
public static EofPayload Create(PayloadData payload)
12+
public static EofPayload Create(ReadOnlySpan<byte> span)
1313
{
14-
var reader = new ByteArrayReader(payload.ArraySegment);
14+
var reader = new ByteArrayReader(span);
1515
reader.ReadByte(Signature);
16-
if (payload.ArraySegment.Count > 5)
16+
if (span.Length > 5)
1717
throw new FormatException("Not an EOF packet");
1818
int warningCount = reader.ReadUInt16();
19-
ServerStatus serverStatus = (ServerStatus) reader.ReadUInt16();
19+
var serverStatus = (ServerStatus) reader.ReadUInt16();
2020

2121
if (reader.BytesRemaining != 0)
2222
throw new FormatException("Extra bytes at end of payload.");

src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Text;
23
using MySql.Data.MySqlClient;
34
using MySqlConnector.Protocol.Serialization;
@@ -14,9 +15,9 @@ internal sealed class ErrorPayload
1415

1516
public MySqlException ToException() => new MySqlException(ErrorCode, State, Message);
1617

17-
public static ErrorPayload Create(PayloadData payload)
18+
public static ErrorPayload Create(ReadOnlySpan<byte> span)
1819
{
19-
var reader = new ByteArrayReader(payload.ArraySegment);
20+
var reader = new ByteArrayReader(span);
2021
reader.ReadByte(Signature);
2122

2223
var errorCode = reader.ReadUInt16();
@@ -25,12 +26,12 @@ public static ErrorPayload Create(PayloadData payload)
2526
if (stateMarker == "#")
2627
{
2728
state = Encoding.ASCII.GetString(reader.ReadByteString(5));
28-
message = Encoding.UTF8.GetString(reader.ReadByteString(payload.ArraySegment.Count - 9));
29+
message = Encoding.UTF8.GetString(reader.ReadByteString(span.Length - 9));
2930
}
3031
else
3132
{
3233
state = "HY000";
33-
message = stateMarker + Encoding.UTF8.GetString(reader.ReadByteString(payload.ArraySegment.Count - 4));
34+
message = stateMarker + Encoding.UTF8.GetString(reader.ReadByteString(span.Length - 4));
3435
}
3536
return new ErrorPayload(errorCode, state, message);
3637
}

src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ internal sealed class InitialHandshakePayload
1313
public byte[] AuthPluginData { get; }
1414
public string AuthPluginName { get; }
1515

16-
public static InitialHandshakePayload Create(PayloadData payload)
16+
public static InitialHandshakePayload Create(ReadOnlySpan<byte> span)
1717
{
18-
var reader = new ByteArrayReader(payload.ArraySegment);
18+
var reader = new ByteArrayReader(span);
1919
reader.ReadByte(c_protocolVersion);
2020
var serverVersion = reader.ReadNullTerminatedByteString();
2121
var connectionId = reader.ReadInt32();

0 commit comments

Comments
 (0)