Skip to content

Commit 0f78099

Browse files
committed
Add ResizableArray.
This allows an old array to be garbage-collected, instead of being kept alive by the existing ColumnDefinitionPayloads that were created.
1 parent 69080de commit 0f78099

File tree

5 files changed

+80
-22
lines changed

5 files changed

+80
-22
lines changed

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ int ReadColumnCount(ArraySegment<byte> arraySegment)
102102
var columnCount = ReadColumnCount(payload.ArraySegment);
103103

104104
// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
105-
Array.Resize(ref m_columnDefinitionPayloads, columnCount * 96);
105+
Utility.Resize(ref m_columnDefinitionPayloads, columnCount * 96);
106106

107107
ColumnDefinitions = new ColumnDefinitionPayload[columnCount];
108108
ColumnTypes = new MySqlDbType[columnCount];
@@ -115,11 +115,11 @@ int ReadColumnCount(ArraySegment<byte> arraySegment)
115115
var arraySegment = payload.ArraySegment;
116116

117117
// 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point
118-
if (m_columnDefinitionPayloadUsedBytes + arraySegment.Count > m_columnDefinitionPayloads.Length)
119-
Array.Resize(ref m_columnDefinitionPayloads, Math.Max(m_columnDefinitionPayloadUsedBytes + arraySegment.Count, m_columnDefinitionPayloadUsedBytes * 2));
120-
Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count);
118+
if (m_columnDefinitionPayloadUsedBytes + arraySegment.Count > m_columnDefinitionPayloads.Count)
119+
Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + arraySegment.Count);
120+
Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, m_columnDefinitionPayloads.Array, m_columnDefinitionPayloadUsedBytes, arraySegment.Count);
121121

122-
var columnDefinition = ColumnDefinitionPayload.Create(new ArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count));
122+
var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count));
123123
ColumnDefinitions[column] = columnDefinition;
124124
ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, treatTinyAsBoolean: Connection.TreatTinyAsBoolean, guidFormat: Connection.GuidFormat);
125125
m_columnDefinitionPayloadUsedBytes += arraySegment.Count;
@@ -344,7 +344,7 @@ public Row GetCurrentRow()
344344
public int? RecordsAffected { get; private set; }
345345
public ResultSetState State { get; private set; }
346346

347-
byte[] m_columnDefinitionPayloads;
347+
ResizableArray<byte> m_columnDefinitionPayloads;
348348
int m_columnDefinitionPayloadUsedBytes;
349349
int[] m_dataLengths;
350350
int[] m_dataOffsets;

src/MySqlConnector/Protocol/Payloads/ColumnDefinitionPayload.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ public string Name
1717
}
1818
}
1919

20-
public CharacterSet CharacterSet { get; private set; }
20+
public CharacterSet CharacterSet { get; }
2121

22-
public uint ColumnLength { get; private set; }
22+
public uint ColumnLength { get; }
2323

24-
public ColumnType ColumnType { get; private set; }
24+
public ColumnType ColumnType { get; }
2525

26-
public ColumnFlags ColumnFlags { get; private set; }
26+
public ColumnFlags ColumnFlags { get; }
2727

2828
public string SchemaName
2929
{
@@ -75,9 +75,9 @@ public string PhysicalName
7575
}
7676
}
7777

78-
public byte Decimals { get; private set; }
78+
public byte Decimals { get; }
7979

80-
public static ColumnDefinitionPayload Create(ArraySegment<byte> arraySegment)
80+
public static ColumnDefinitionPayload Create(ResizableArraySegment<byte> arraySegment)
8181
{
8282
var reader = new ByteArrayReader(arraySegment);
8383
SkipLengthEncodedByteString(ref reader); // catalog
@@ -95,15 +95,7 @@ public static ColumnDefinitionPayload Create(ArraySegment<byte> arraySegment)
9595
reader.ReadByte(0); // reserved byte 1
9696
reader.ReadByte(0); // reserved byte 2
9797

98-
return new ColumnDefinitionPayload
99-
{
100-
OriginalData = arraySegment,
101-
CharacterSet = characterSet,
102-
ColumnLength = columnLength,
103-
ColumnType = columnType,
104-
ColumnFlags = columnFlags,
105-
Decimals = decimals
106-
};
98+
return new ColumnDefinitionPayload(arraySegment, characterSet, columnLength, columnType, columnFlags, decimals);
10799
}
108100

109101
private static void SkipLengthEncodedByteString(ref ByteArrayReader reader)
@@ -112,6 +104,16 @@ private static void SkipLengthEncodedByteString(ref ByteArrayReader reader)
112104
reader.Offset += length;
113105
}
114106

107+
private ColumnDefinitionPayload(ResizableArraySegment<byte> originalData, CharacterSet characterSet, uint columnLength, ColumnType columnType, ColumnFlags columnFlags, byte decimals)
108+
{
109+
OriginalData = originalData;
110+
CharacterSet = characterSet;
111+
ColumnLength = columnLength;
112+
ColumnType = columnType;
113+
ColumnFlags = columnFlags;
114+
Decimals = decimals;
115+
}
116+
115117
private void ReadNames()
116118
{
117119
var reader = new ByteArrayReader(OriginalData);
@@ -124,7 +126,7 @@ private void ReadNames()
124126
m_readNames = true;
125127
}
126128

127-
ArraySegment<byte> OriginalData { get; set; }
129+
ResizableArraySegment<byte> OriginalData { get; }
128130

129131
bool m_readNames;
130132
string m_name;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace MySqlConnector.Utilities
2+
{
3+
/// <summary>
4+
/// A wrapper around a resizable array. This type is intended to be used with <see cref="ResizableArraySegment{T}"/>.
5+
/// </summary>
6+
internal sealed class ResizableArray<T>
7+
{
8+
public T[] Array => m_array;
9+
public int Count => m_array?.Length ?? 0;
10+
11+
/// <summary>
12+
/// Do not call this method directly; use <see cref="Utility.Resize{T}"/>.
13+
/// </summary>
14+
internal void DoResize(int length)
15+
{
16+
if (m_array == null || length > m_array.Length)
17+
System.Array.Resize(ref m_array, length);
18+
}
19+
20+
T[] m_array;
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace MySqlConnector.Utilities
4+
{
5+
/// <summary>
6+
/// An <see cref="ArraySegment{T}"/> that supports having its underlying array reallocated and resized.
7+
/// </summary>
8+
internal struct ResizableArraySegment<T>
9+
{
10+
public ResizableArraySegment(ResizableArray<T> array, int offset, int count)
11+
{
12+
Array = array;
13+
Offset = offset;
14+
Count = count;
15+
}
16+
17+
public ResizableArray<T> Array { get; }
18+
public int Offset { get; }
19+
public int Count { get; }
20+
21+
public static implicit operator ReadOnlySpan<T>(ResizableArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array.Array, segment.Offset, segment.Count);
22+
}
23+
}

src/MySqlConnector/Utilities/Utility.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,17 @@ public static int FindNextIndex(ReadOnlySpan<byte> data, int offset, ReadOnlySpa
230230
return -1;
231231
}
232232

233+
/// <summary>
234+
/// Resizes <paramref name="resizableArray"/> to hold at least <paramref name="newLength"/> items.
235+
/// </summary>
236+
/// <remarks><paramref name="resizableArray"/> may be <c>null</c>, in which case a new <see cref="ResizableArray{T}"/> will be allocated.</remarks>
237+
public static void Resize<T>(ref ResizableArray<T> resizableArray, int newLength)
238+
{
239+
if (resizableArray == null)
240+
resizableArray = new ResizableArray<T>();
241+
resizableArray.DoResize(Math.Max(newLength, resizableArray.Count * 2));
242+
}
243+
233244
public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)
234245
{
235246
var originalValue = value;

0 commit comments

Comments
 (0)