Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize the SqlGuid struct #72549

Merged
merged 15 commits into from
Jan 30, 2023
5 changes: 3 additions & 2 deletions src/libraries/System.Data.Common/ref/System.Data.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3213,15 +3213,15 @@ void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter wri
public override string ToString() { throw null; }
}
[System.Xml.Serialization.XmlSchemaProviderAttribute("GetXsdType")]
public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparable, System.Xml.Serialization.IXmlSerializable, System.IEquatable<System.Data.SqlTypes.SqlGuid>
public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparable, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable, System.IEquatable<System.Data.SqlTypes.SqlGuid>
{
private object _dummy;
private int _dummyPrimitive;
public static readonly System.Data.SqlTypes.SqlGuid Null;
public SqlGuid(byte[] value) { throw null; }
public SqlGuid(System.Guid g) { throw null; }
public SqlGuid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) { throw null; }
public SqlGuid(string s) { throw null; }
public SqlGuid(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext sc) { throw null; }
public bool IsNull { get { throw null; } }
public System.Guid Value { get { throw null; } }
public int CompareTo(System.Data.SqlTypes.SqlGuid value) { throw null; }
Expand Down Expand Up @@ -3250,6 +3250,7 @@ public partial struct SqlGuid : System.Data.SqlTypes.INullable, System.IComparab
System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; }
void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { }
void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { }
void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext sc) { }
public byte[]? ToByteArray() { throw null; }
public System.Data.SqlTypes.SqlBinary ToSqlBinary() { throw null; }
public System.Data.SqlTypes.SqlString ToSqlString() { throw null; }
Expand Down
101 changes: 48 additions & 53 deletions src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLGuid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Data.Common;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
Expand All @@ -16,70 +20,52 @@ namespace System.Data.SqlTypes
[Serializable]
[XmlSchemaProvider("GetXsdType")]
[System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public struct SqlGuid : INullable, IComparable, IXmlSerializable, IEquatable<SqlGuid>
public struct SqlGuid : INullable, IComparable, ISerializable, IXmlSerializable, IEquatable<SqlGuid>
{
private const int SizeOfGuid = 16;
private const int SizeOfGuid = 16; // sizeof(Guid)

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

// constructor
// construct a SqlGuid.Null
private SqlGuid(bool fNull)
{
m_value = null;
}

public SqlGuid(byte[] value)
{
if (value == null || value.Length != SizeOfGuid)
throw new ArgumentException(SQLResource.InvalidArraySizeMessage);

m_value = new byte[SizeOfGuid];
value.CopyTo(m_value, 0);
}

internal SqlGuid(byte[] value, bool ignored)
{
if (value == null || value.Length != SizeOfGuid)
throw new ArgumentException(SQLResource.InvalidArraySizeMessage);

m_value = value;
_value = new Guid(value);
}

public SqlGuid(string s)
{
m_value = (new Guid(s)).ToByteArray();
_value = new Guid(s);
}

public SqlGuid(Guid g)
{
m_value = g.ToByteArray();
_value = g;
}

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


// INullable
public bool IsNull
public SqlGuid(SerializationInfo si, StreamingContext sc)
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
{
get { return (m_value is null); }
byte[]? value = (byte[]?)si.GetValue("m_value", typeof(byte[]));
if (value is null)
_value = null;
else
_value = new Guid(value);
}

// INullable
public bool IsNull => _value is null;

// property: Value
public Guid Value
{
get
{
if (m_value is null)
throw new SqlNullValueException();
else
return new Guid(m_value);
}
}
public Guid Value => _value ?? throw new SqlNullValueException();

// Implicit conversion from Guid to SqlGuid
public static implicit operator SqlGuid(Guid x)
Expand All @@ -95,18 +81,17 @@ public static explicit operator Guid(SqlGuid x)

public byte[]? ToByteArray()
{
byte[] ret = new byte[SizeOfGuid];
m_value!.CopyTo(ret, 0); // TODO: NRE
return ret;
if (_value is null)
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
return null;
return _value.GetValueOrDefault().ToByteArray();
}

public override string ToString()
{
if (m_value is null)
if (_value is null)
return SQLResource.NullString;

Guid g = new Guid(m_value);
return g.ToString();
return _value.GetValueOrDefault().ToString();
}

public static SqlGuid Parse(string s)
Expand All @@ -117,16 +102,24 @@ public static SqlGuid Parse(string s)
return new SqlGuid(s);
}


// Comparison operators
private static EComparison Compare(SqlGuid x, SqlGuid y)
{
// Comparison orders.
ReadOnlySpan<byte> rgiGuidOrder = new byte[16] { 10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3 };
ReadOnlySpan<byte> rgiGuidOrder = new byte[SizeOfGuid] { 10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3 };

Debug.Assert(!x.IsNull);
Debug.Assert(!y.IsNull);

// Swap to the correct order to be compared
ReadOnlySpan<byte> xBytes = x.m_value;
ReadOnlySpan<byte> yBytes = y.m_value;
Span<byte> xBytes = stackalloc byte[SizeOfGuid];
bool xWrote = x._value.GetValueOrDefault().TryWriteBytes(xBytes);
Debug.Assert(xWrote);

Span<byte> yBytes = stackalloc byte[SizeOfGuid];
bool yWrote = y._value.GetValueOrDefault().TryWriteBytes(yBytes);
Debug.Assert(yWrote);

for (int i = 0; i < SizeOfGuid; i++)
{
byte b1 = xBytes[rgiGuidOrder[i]];
Expand All @@ -140,8 +133,6 @@ private static EComparison Compare(SqlGuid x, SqlGuid y)
return EComparison.EQ;
}



// Implicit conversions

// Explicit conversions
Expand Down Expand Up @@ -249,7 +240,6 @@ public SqlBinary ToSqlBinary()
return (SqlBinary)this;
}


// IComparable
// Compares this object to another object, returning an integer that
// indicates the relationship.
Expand Down Expand Up @@ -292,7 +282,7 @@ public bool Equals(SqlGuid other) =>
(this == other).Value;

// For hashing purpose
public override int GetHashCode() => IsNull ? 0 : Value.GetHashCode();
public override int GetHashCode() => IsNull ? 0 : _value.GetValueOrDefault().GetHashCode();
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved

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

Expand All @@ -303,23 +293,23 @@ void IXmlSerializable.ReadXml(XmlReader reader)
{
// Read the next value.
reader.ReadElementString();
m_value = null;
_value = null;
}
else
{
m_value = new Guid(reader.ReadElementString()).ToByteArray();
_value = new Guid(reader.ReadElementString());
}
}

void IXmlSerializable.WriteXml(XmlWriter writer)
{
if (m_value is null)
if (_value is null)
{
writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
}
else
{
writer.WriteString(XmlConvert.ToString(new Guid(m_value)));
writer.WriteString(XmlConvert.ToString(_value.GetValueOrDefault()));
}
}

Expand All @@ -328,6 +318,11 @@ public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet)
return new XmlQualifiedName("string", XmlSchema.Namespace);
}

public static readonly SqlGuid Null = new SqlGuid(true);
void ISerializable.GetObjectData(SerializationInfo si, StreamingContext sc)
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
{
si.AddValue("m_value", ToByteArray());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the behavior of ToByteArray is reverted, this will have to change.

}

public static readonly SqlGuid Null;
} // SqlGuid
} // namespace System.Data.SqlTypes