Skip to content

Commit b324d4d

Browse files
committed
Change PayloadData to use ReadOnlyMemory<byte>.
This better expresses that the memory it wraps is non-null.
1 parent 04784ef commit b324d4d

File tree

13 files changed

+100
-97
lines changed

13 files changed

+100
-97
lines changed

src/MySqlConnector/Core/CommandExecutor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ public static async Task<DbDataReader> ExecuteReaderAsync(IReadOnlyList<IMySqlCo
5757
Log.Warn("Session{0} query was interrupted", command.Connection.Session.Id);
5858
throw new OperationCanceledException(cancellationToken);
5959
}
60-
catch (Exception ex) when (payload.ArraySegment.Count > 4_194_304 && (ex is SocketException || ex is IOException || ex is MySqlProtocolException))
60+
catch (Exception ex) when (payload.Span.Length > 4_194_304 && (ex is SocketException || ex is IOException || ex is MySqlProtocolException))
6161
{
6262
// the default MySQL Server value for max_allowed_packet (in MySQL 5.7) is 4MiB: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_allowed_packet
6363
// use "decimal megabytes" (to round up) when creating the exception message
64-
int megabytes = payload.ArraySegment.Count / 1_000_000;
64+
int megabytes = payload.Span.Length / 1_000_000;
6565
throw new MySqlException("Error submitting {0}MB packet; ensure 'max_allowed_packet' is greater than {0}MB.".FormatInvariant(megabytes), ex);
6666
}
6767
}

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
5353
var firstByte = payload.HeaderByte;
5454
if (firstByte == OkPayload.Signature)
5555
{
56-
var ok = OkPayload.Create(payload.AsSpan(), Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
56+
var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
5757
RecordsAffected = (RecordsAffected ?? 0) + ok.AffectedRowCount;
5858
LastInsertId = unchecked((long) ok.LastInsertId);
5959
WarningCount = ok.WarningCount;
@@ -73,7 +73,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
7373
{
7474
if (!Connection.AllowLoadLocalInfile)
7575
throw new NotSupportedException("To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
76-
var localInfile = LocalInfilePayload.Create(payload.AsSpan());
76+
var localInfile = LocalInfilePayload.Create(payload.Span);
7777
if (!IsHostVerified(Connection)
7878
&& !localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal))
7979
throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE. See https://fl.vu/mysql-load-data");
@@ -109,7 +109,7 @@ int ReadColumnCount(ReadOnlySpan<byte> span)
109109
throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324");
110110
return columnCount_;
111111
}
112-
var columnCount = ReadColumnCount(payload.AsSpan());
112+
var columnCount = ReadColumnCount(payload.Span);
113113

114114
// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
115115
Utility.Resize(ref m_columnDefinitionPayloads, columnCount * 96);
@@ -120,23 +120,23 @@ int ReadColumnCount(ReadOnlySpan<byte> span)
120120
for (var column = 0; column < ColumnDefinitions.Length; column++)
121121
{
122122
payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
123-
var arraySegment = payload.ArraySegment;
123+
var payloadLength = payload.Span.Length;
124124

125125
// 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point
126-
if (m_columnDefinitionPayloadUsedBytes + arraySegment.Count > m_columnDefinitionPayloads.Count)
127-
Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + arraySegment.Count);
128-
Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, m_columnDefinitionPayloads.Array, m_columnDefinitionPayloadUsedBytes, arraySegment.Count);
126+
if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloads.Count)
127+
Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + payloadLength);
128+
payload.Span.CopyTo(m_columnDefinitionPayloads.Array.AsSpan().Slice(m_columnDefinitionPayloadUsedBytes));
129129

130-
var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count));
130+
var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength));
131131
ColumnDefinitions[column] = columnDefinition;
132132
ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, treatTinyAsBoolean: Connection.TreatTinyAsBoolean, guidFormat: Connection.GuidFormat);
133-
m_columnDefinitionPayloadUsedBytes += arraySegment.Count;
133+
m_columnDefinitionPayloadUsedBytes += payloadLength;
134134
}
135135

136136
if (!Session.SupportsDeprecateEof)
137137
{
138138
payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
139-
EofPayload.Create(payload.AsSpan());
139+
EofPayload.Create(payload.Span);
140140
}
141141

142142
if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
@@ -245,7 +245,7 @@ static Row ScanRowAsyncRemainder(ResultSet this_, PayloadData payload, Row row_)
245245
{
246246
if (payload.HeaderByte == EofPayload.Signature)
247247
{
248-
var span = payload.AsSpan();
248+
var span = payload.Span;
249249
if (this_.Session.SupportsDeprecateEof && OkPayload.IsOk(span, this_.Session.SupportsDeprecateEof))
250250
{
251251
var ok = OkPayload.Create(span, this_.Session.SupportsDeprecateEof, this_.Session.SupportsSessionTrack);
@@ -270,7 +270,7 @@ static Row ScanRowAsyncRemainder(ResultSet this_, PayloadData payload, Row row_)
270270
// this might be a binary row, but it might also be a text row whose first column is zero bytes long; try reading
271271
// the row as a series of length-encoded values (the text format) to see if this might plausibly be a text row
272272
var isTextRow = false;
273-
var reader = new ByteArrayReader(payload.AsSpan());
273+
var reader = new ByteArrayReader(payload.Span);
274274
var columnCount = 0;
275275
while (reader.BytesRemaining > 0)
276276
{
@@ -331,7 +331,7 @@ static Row ScanRowAsyncRemainder(ResultSet this_, PayloadData payload, Row row_)
331331
}
332332
row_ = isBinaryRow ? (Row) new BinaryRow(this_) : new TextRow(this_);
333333
}
334-
row_.SetData(payload.ArraySegment);
334+
row_.SetData(payload.Memory);
335335
this_.m_rowBuffered = row_;
336336
this_.m_hasRows = true;
337337
this_.BufferState = ResultSetState.ReadingRows;

src/MySqlConnector/Core/Row.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#nullable disable
22
using System;
33
using System.IO;
4+
using System.Runtime.InteropServices;
45
using MySql.Data.MySqlClient;
56
using MySql.Data.Types;
67
using MySqlConnector.Protocol;
@@ -11,23 +12,23 @@ namespace MySqlConnector.Core
1112
{
1213
internal abstract class Row
1314
{
14-
public void SetData(ArraySegment<byte> data)
15+
public void SetData(ReadOnlyMemory<byte> data)
1516
{
1617
m_data = data;
1718
if (m_dataOffsets is null)
1819
{
1920
m_dataOffsets = new int[ResultSet.ColumnDefinitions.Length];
2021
m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
2122
}
22-
GetDataOffsets(m_data.AsSpan(), m_dataOffsets, m_dataLengths);
23+
GetDataOffsets(m_data.Span, m_dataOffsets, m_dataLengths);
2324
}
2425

2526
public Row Clone()
2627
{
2728
var clonedRow = CloneCore();
28-
var clonedData = new byte[m_data.Count];
29-
Buffer.BlockCopy(m_data.Array, m_data.Offset, clonedData, 0, m_data.Count);
30-
clonedRow.SetData(new ArraySegment<byte>(clonedData));
29+
var clonedData = new byte[m_data.Length];
30+
m_data.CopyTo(clonedData);
31+
clonedRow.SetData(clonedData);
3132
return clonedRow;
3233
}
3334

@@ -39,7 +40,7 @@ public object GetValue(int ordinal)
3940
if (m_dataOffsets[ordinal] == -1)
4041
return DBNull.Value;
4142

42-
var data = new ReadOnlySpan<byte>(m_data.Array, m_data.Offset + m_dataOffsets[ordinal], m_dataLengths[ordinal]);
43+
var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
4344
var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
4445
return GetValueCore(data, columnDefinition);
4546
}
@@ -88,7 +89,8 @@ public long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffs
8889

8990
var offset = (int) dataOffset;
9091
var lengthToCopy = Math.Max(0, Math.Min(m_dataLengths[ordinal] - offset, length));
91-
Buffer.BlockCopy(m_data.Array, m_data.Offset + m_dataOffsets[ordinal] + offset, buffer, bufferOffset, lengthToCopy);
92+
if (lengthToCopy > 0)
93+
m_data.Slice(m_dataOffsets[ordinal] + offset, lengthToCopy).Span.CopyTo(buffer.AsSpan().Slice(bufferOffset));
9294
return lengthToCopy;
9395
}
9496

@@ -291,7 +293,9 @@ public DateTime GetDateTime(int ordinal)
291293
public Stream GetStream(int ordinal)
292294
{
293295
CheckBinaryColumn(ordinal);
294-
return new MemoryStream(m_data.Array, m_data.Offset + m_dataOffsets[ordinal], m_dataLengths[ordinal], false);
296+
return (MemoryMarshal.TryGetArray(m_data, out var arraySegment)) ?
297+
new MemoryStream(arraySegment.Array, arraySegment.Offset + m_dataOffsets[ordinal], m_dataLengths[ordinal], writable: false) :
298+
throw new InvalidOperationException("Can't get underlying array.");
295299
}
296300

297301
public string GetString(int ordinal) => (string) GetValue(ordinal);
@@ -405,7 +409,7 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
405409
throw new ArgumentException("bufferOffset + length cannot exceed buffer.Length", nameof(length));
406410
}
407411

408-
ArraySegment<byte> m_data;
412+
ReadOnlyMemory<byte> m_data;
409413
int[] m_dataOffsets;
410414
int[] m_dataLengths;
411415
}

0 commit comments

Comments
 (0)