Skip to content

Commit bf5afd4

Browse files
Optimize the SqlGuid struct (#72549)
* Optimize the SqlGuid struct Optimizes the SqlGuid struct according to the idea from #51836. * Update SQLGuid.cs * Apply suggestions * Update SQLGuid.cs * Update SQLGuid.cs * Update ref assembly * Update System.Data.Common.cs * Apply suggestions * Update SQLGuid.cs * Update SQLGuid.cs * Update SQLGuid.cs * Add a comment about binary serialization * Expand on the comment for backwards-compatible binary serialization --------- Co-authored-by: Jeff Handley <[email protected]>
1 parent 5d604a8 commit bf5afd4

File tree

2 files changed

+55
-59
lines changed

2 files changed

+55
-59
lines changed

src/libraries/System.Data.Common/ref/System.Data.Common.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,9 +3224,8 @@ void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter wri
32243224
public override string ToString() { throw null; }
32253225
}
32263226
[System.Xml.Serialization.XmlSchemaProviderAttribute("GetXsdType")]
3227-
public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparable, System.Xml.Serialization.IXmlSerializable, System.IEquatable<System.Data.SqlTypes.SqlGuid>
3227+
public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparable, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable, System.IEquatable<System.Data.SqlTypes.SqlGuid>
32283228
{
3229-
private object _dummy;
32303229
private int _dummyPrimitive;
32313230
public static readonly System.Data.SqlTypes.SqlGuid Null;
32323231
public SqlGuid(byte[] value) { throw null; }
@@ -3261,6 +3260,7 @@ public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparab
32613260
System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; }
32623261
void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { }
32633262
void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { }
3263+
void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
32643264
public byte[]? ToByteArray() { throw null; }
32653265
public System.Data.SqlTypes.SqlBinary ToSqlBinary() { throw null; }
32663266
public System.Data.SqlTypes.SqlString ToSqlString() { throw null; }

src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLGuid.cs

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Data.Common;
5+
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
using System.Runtime.Serialization;
59
using System.Xml;
610
using System.Xml.Schema;
711
using System.Xml.Serialization;
@@ -16,71 +20,60 @@ namespace System.Data.SqlTypes
1620
[Serializable]
1721
[XmlSchemaProvider("GetXsdType")]
1822
[System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
19-
public struct SqlGuid : INullable, IComparable, IXmlSerializable, IEquatable<SqlGuid>
23+
public struct SqlGuid : INullable, IComparable, ISerializable, IXmlSerializable, IEquatable<SqlGuid>
2024
{
21-
private const int SizeOfGuid = 16;
25+
private const int SizeOfGuid = 16; // sizeof(Guid)
2226

2327
// NOTE: If any instance fields change, update SqlTypeWorkarounds type in System.Data.SqlClient.
24-
private byte[]? m_value; // the SqlGuid is null if m_value is null
28+
private Guid? _value; // the SqlGuid is null if _value is null
2529

2630
// constructor
27-
// construct a SqlGuid.Null
28-
private SqlGuid(bool _)
29-
{
30-
m_value = null;
31-
}
32-
3331
public SqlGuid(byte[] value)
3432
{
3533
if (value == null || value.Length != SizeOfGuid)
3634
throw new ArgumentException(SQLResource.InvalidArraySizeMessage);
3735

38-
m_value = new byte[SizeOfGuid];
39-
value.CopyTo(m_value, 0);
40-
}
41-
42-
internal SqlGuid(byte[] value, bool _)
43-
{
44-
if (value == null || value.Length != SizeOfGuid)
45-
throw new ArgumentException(SQLResource.InvalidArraySizeMessage);
46-
47-
m_value = value;
36+
_value = new Guid(value);
4837
}
4938

5039
public SqlGuid(string s)
5140
{
52-
m_value = (new Guid(s)).ToByteArray();
41+
_value = new Guid(s);
5342
}
5443

5544
public SqlGuid(Guid g)
5645
{
57-
m_value = g.ToByteArray();
46+
_value = g;
5847
}
5948

6049
public SqlGuid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k)
6150
: this(new Guid(a, b, c, d, e, f, g, h, i, j, k))
6251
{
6352
}
6453

65-
66-
// INullable
67-
public bool IsNull
54+
// Maintains backwards-compatible binary serialization for the `private byte[] m_value` field
55+
// see src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs
56+
// for test data
57+
private SqlGuid(SerializationInfo info, StreamingContext context)
6858
{
69-
get { return (m_value is null); }
59+
byte[]? value = (byte[]?)info.GetValue("m_value", typeof(byte[]));
60+
if (value is null)
61+
_value = null;
62+
else
63+
_value = new Guid(value);
7064
}
7165

72-
// property: Value
73-
public Guid Value
66+
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
7467
{
75-
get
76-
{
77-
if (m_value is null)
78-
throw new SqlNullValueException();
79-
else
80-
return new Guid(m_value);
81-
}
68+
info.AddValue("m_value", ToByteArray(), typeof(byte[]));
8269
}
8370

71+
// INullable
72+
public bool IsNull => _value is null;
73+
74+
// property: Value
75+
public Guid Value => _value ?? throw new SqlNullValueException();
76+
8477
// Implicit conversion from Guid to SqlGuid
8578
public static implicit operator SqlGuid(Guid x)
8679
{
@@ -95,18 +88,17 @@ public static explicit operator Guid(SqlGuid x)
9588

9689
public byte[]? ToByteArray()
9790
{
98-
byte[] ret = new byte[SizeOfGuid];
99-
m_value!.CopyTo(ret, 0); // TODO: NRE
100-
return ret;
91+
if (_value is null)
92+
return null;
93+
return _value.GetValueOrDefault().ToByteArray();
10194
}
10295

10396
public override string ToString()
10497
{
105-
if (m_value is null)
98+
if (_value is null)
10699
return SQLResource.NullString;
107100

108-
Guid g = new Guid(m_value);
109-
return g.ToString();
101+
return _value.GetValueOrDefault().ToString();
110102
}
111103

112104
public static SqlGuid Parse(string s)
@@ -117,16 +109,24 @@ public static SqlGuid Parse(string s)
117109
return new SqlGuid(s);
118110
}
119111

120-
121112
// Comparison operators
122113
private static EComparison Compare(SqlGuid x, SqlGuid y)
123114
{
124115
// Comparison orders.
125-
ReadOnlySpan<byte> rgiGuidOrder = new byte[16] { 10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3 };
116+
ReadOnlySpan<byte> rgiGuidOrder = new byte[SizeOfGuid] { 10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3 };
117+
118+
Debug.Assert(!x.IsNull);
119+
Debug.Assert(!y.IsNull);
126120

127121
// Swap to the correct order to be compared
128-
ReadOnlySpan<byte> xBytes = x.m_value;
129-
ReadOnlySpan<byte> yBytes = y.m_value;
122+
Span<byte> xBytes = stackalloc byte[SizeOfGuid];
123+
bool xWrote = x._value.GetValueOrDefault().TryWriteBytes(xBytes);
124+
Debug.Assert(xWrote);
125+
126+
Span<byte> yBytes = stackalloc byte[SizeOfGuid];
127+
bool yWrote = y._value.GetValueOrDefault().TryWriteBytes(yBytes);
128+
Debug.Assert(yWrote);
129+
130130
for (int i = 0; i < SizeOfGuid; i++)
131131
{
132132
byte b1 = xBytes[rgiGuidOrder[i]];
@@ -140,8 +140,6 @@ private static EComparison Compare(SqlGuid x, SqlGuid y)
140140
return EComparison.EQ;
141141
}
142142

143-
144-
145143
// Implicit conversions
146144

147145
// Explicit conversions
@@ -161,7 +159,8 @@ public static explicit operator SqlGuid(SqlBinary x)
161159
// Overloading comparison operators
162160
public static SqlBoolean operator ==(SqlGuid x, SqlGuid y)
163161
{
164-
return (x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.EQ);
162+
return (x.IsNull || y.IsNull) ? SqlBoolean.Null :
163+
new SqlBoolean(x._value.GetValueOrDefault() == y._value.GetValueOrDefault());
165164
}
166165

167166
public static SqlBoolean operator !=(SqlGuid x, SqlGuid y)
@@ -249,7 +248,6 @@ public SqlBinary ToSqlBinary()
249248
return (SqlBinary)this;
250249
}
251250

252-
253251
// IComparable
254252
// Compares this object to another object, returning an integer that
255253
// indicates the relationship.
@@ -287,12 +285,10 @@ public override bool Equals([NotNullWhen(true)] object? value) =>
287285
/// <summary>Indicates whether the current instance is equal to another instance of the same type.</summary>
288286
/// <param name="other">An instance to compare with this instance.</param>
289287
/// <returns>true if the current instance is equal to the other instance; otherwise, false.</returns>
290-
public bool Equals(SqlGuid other) =>
291-
other.IsNull || IsNull ? other.IsNull && IsNull :
292-
(this == other).Value;
288+
public bool Equals(SqlGuid other) => _value == other._value;
293289

294290
// For hashing purpose
295-
public override int GetHashCode() => IsNull ? 0 : Value.GetHashCode();
291+
public override int GetHashCode() => _value.GetHashCode();
296292

297293
XmlSchema? IXmlSerializable.GetSchema() { return null; }
298294

@@ -303,23 +299,23 @@ void IXmlSerializable.ReadXml(XmlReader reader)
303299
{
304300
// Read the next value.
305301
reader.ReadElementString();
306-
m_value = null;
302+
_value = null;
307303
}
308304
else
309305
{
310-
m_value = new Guid(reader.ReadElementString()).ToByteArray();
306+
_value = new Guid(reader.ReadElementString());
311307
}
312308
}
313309

314310
void IXmlSerializable.WriteXml(XmlWriter writer)
315311
{
316-
if (m_value is null)
312+
if (_value is null)
317313
{
318314
writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
319315
}
320316
else
321317
{
322-
writer.WriteString(XmlConvert.ToString(new Guid(m_value)));
318+
writer.WriteString(XmlConvert.ToString(_value.GetValueOrDefault()));
323319
}
324320
}
325321

@@ -328,6 +324,6 @@ public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet)
328324
return new XmlQualifiedName("string", XmlSchema.Namespace);
329325
}
330326

331-
public static readonly SqlGuid Null = new SqlGuid(true);
327+
public static readonly SqlGuid Null;
332328
} // SqlGuid
333329
} // namespace System.Data.SqlTypes

0 commit comments

Comments
 (0)