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
109 changes: 60 additions & 49 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,68 +20,65 @@ 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)
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
{
m_value = null;
_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
private SqlGuid(SerializationInfo si, StreamingContext sc)
{
get { return (m_value is null); }
ArgumentNullException.ThrowIfNull(si);
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
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
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
{
get
{
if (m_value is null)
if (_value is null)
throw new SqlNullValueException();
else
return new Guid(m_value);
return _value.GetValueOrDefault();
}
}

Expand All @@ -95,18 +96,17 @@ public Guid Value

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,31 +117,37 @@ 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 };

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[sizeof(Guid)];
bool xWrote = x._value.GetValueOrDefault().TryWriteBytes(xBytes);
Debug.Assert(xWrote);

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

for (int i = 0; i < SizeOfGuid; i++)
{
byte b1 = xBytes[rgiGuidOrder[i]];
byte b2 = yBytes[rgiGuidOrder[i]];
if (b1 != b2)
{
return (b1 < b2) ? EComparison.LT : EComparison.GT;
return b1 < b2 ? EComparison.LT : EComparison.GT;
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
}
}

return EComparison.EQ;
}



// Implicit conversions

// Explicit conversions
Expand All @@ -161,7 +167,7 @@ private static EComparison Compare(SqlGuid x, SqlGuid y)
// Overloading comparison operators
public static SqlBoolean operator ==(SqlGuid x, SqlGuid y)
{
return (x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.EQ);
return x.IsNull || y.IsNull ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.EQ);
}

public static SqlBoolean operator !=(SqlGuid x, SqlGuid y)
Expand All @@ -171,12 +177,12 @@ private static EComparison Compare(SqlGuid x, SqlGuid y)

public static SqlBoolean operator <(SqlGuid x, SqlGuid y)
{
return (x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.LT);
return x.IsNull || y.IsNull ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.LT);
}

public static SqlBoolean operator >(SqlGuid x, SqlGuid y)
{
return (x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.GT);
return x.IsNull || y.IsNull ? SqlBoolean.Null : new SqlBoolean(Compare(x, y) == EComparison.GT);
}

public static SqlBoolean operator <=(SqlGuid x, SqlGuid y)
Expand Down Expand Up @@ -204,37 +210,37 @@ private static EComparison Compare(SqlGuid x, SqlGuid y)
// Alternative method for operator ==
public static SqlBoolean Equals(SqlGuid x, SqlGuid y)
{
return (x == y);
return x == y;
}

// Alternative method for operator !=
public static SqlBoolean NotEquals(SqlGuid x, SqlGuid y)
{
return (x != y);
return x != y;
}

// Alternative method for operator <
public static SqlBoolean LessThan(SqlGuid x, SqlGuid y)
{
return (x < y);
return x < y;
}

// Alternative method for operator >
public static SqlBoolean GreaterThan(SqlGuid x, SqlGuid y)
{
return (x > y);
return x > y;
}

// Alternative method for operator <=
public static SqlBoolean LessThanOrEqual(SqlGuid x, SqlGuid y)
{
return (x <= y);
return x <= y;
}

// Alternative method for operator >=
public static SqlBoolean GreaterThanOrEqual(SqlGuid x, SqlGuid y)
{
return (x >= y);
return x >= y;
}

// Alternative method for conversions.
Expand All @@ -249,7 +255,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 +297,7 @@ public int CompareTo(SqlGuid value)
(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 +308,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 +333,12 @@ public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet)
return new XmlQualifiedName("string", XmlSchema.Namespace);
}

void ISerializable.GetObjectData(SerializationInfo si, StreamingContext sc)
MichalPetryka marked this conversation as resolved.
Show resolved Hide resolved
{
ArgumentNullException.ThrowIfNull(si);
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 = new SqlGuid(true);
} // SqlGuid
} // namespace System.Data.SqlTypes