Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add datetime read span path for netcore #31044

Merged
merged 18 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/System.Data.SqlClient/src/System.Data.SqlClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@
</ItemGroup>
<!-- Windows dependencies for Integrated Authentication for MANAGED_SNI build -->
<ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(IsPartialFacadeAssembly)' != 'true' ">
<Reference Include="System.Memory" />
<Compile Include="$(CommonPath)\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs">
<Link>Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs</Link>
</Compile>
Expand Down Expand Up @@ -404,6 +403,7 @@
</ItemGroup>
<!-- Common (Windows and Unix) dependencies for MANAGED_SNI build -->
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true' AND '$(OSGroup)' != 'AnyOS'">
<Reference Include="System.Memory" />
Copy link
Member

Choose a reason for hiding this comment

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

@safern, should this be under the ItemGroup that is just Condition="'$(IsPartialFacadeAssembly)' != 'true'? Why do we need to avoid adding the reference to System.Memory for AnyOS?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry I missed the notification to this question.

We don't need to avoid the reference, but it doesn't make sense to add the reference for AnyOS as we're not exposing any public API that uses System.Memory and when AnyOS == true we generate a platform not supported exception, so basically we would just be bringing a non-used dependency to the turd assembly.

<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(OSGroup)' == 'AnyOS'">SR.PlatformNotSupported_DataSqlClient</GeneratePlatformNotSupportedAssemblyMessage>

<Compile Include="$(CommonPath)\System\Net\ContextFlagsPal.cs">
<Link>Common\System\Net\ContextFlagsPal.cs</Link>
</Compile>
Expand Down
76 changes: 17 additions & 59 deletions src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,90 +1027,47 @@ internal void SetToString(string value)
_isNull = false;
}

internal void SetToDate(byte[] bytes)
internal void SetToDate(ReadOnlySpan<byte> bytes)
{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.Date;
_value._int32 = GetDateFromByteArray(bytes, 0);
_value._int32 = GetDateFromByteArray(bytes);
_isNull = false;
}

internal void SetToDate(DateTime date)
internal void SetToTime(ReadOnlySpan<byte> bytes, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.Date;
_value._int32 = date.Subtract(DateTime.MinValue).Days;
_isNull = false;
}

internal void SetToTime(byte[] bytes, int length, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.Time;
FillInTimeInfo(ref _value._timeInfo, bytes, length, scale);
FillInTimeInfo(ref _value._timeInfo, bytes, scale);
_isNull = false;
}

internal void SetToTime(TimeSpan timeSpan, byte scale)
internal void SetToDateTime2(ReadOnlySpan<byte> bytes, byte scale)
Copy link
Member

Choose a reason for hiding this comment

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

What does the 2 mean in this method name? It's not very descriptive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It means that it's using the data layout for an sql type SQLDATETIME2, which represents a datetime2 and I agree that it's a silly name but that's what it's called.

Copy link
Member

Choose a reason for hiding this comment

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

Ugh. Ok, thanks.

{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.Time;
_value._timeInfo.ticks = timeSpan.Ticks;
_value._timeInfo.scale = scale;
_isNull = false;
}

internal void SetToDateTime2(byte[] bytes, int length, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

int length = bytes.Length;
_type = StorageType.DateTime2;
FillInTimeInfo(ref _value._dateTime2Info.timeInfo, bytes, length - 3, scale); // remaining 3 bytes is for date
_value._dateTime2Info.date = GetDateFromByteArray(bytes, length - 3); // 3 bytes for date
FillInTimeInfo(ref _value._dateTime2Info.timeInfo, bytes.Slice(0, length - 3), scale); // remaining 3 bytes is for date
_value._dateTime2Info.date = GetDateFromByteArray(bytes.Slice(length - 3)); // 3 bytes for date
_isNull = false;
}

internal void SetToDateTime2(DateTime dateTime, byte scale)
internal void SetToDateTimeOffset(ReadOnlySpan<byte> bytes, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.DateTime2;
_value._dateTime2Info.timeInfo.ticks = dateTime.TimeOfDay.Ticks;
_value._dateTime2Info.timeInfo.scale = scale;
_value._dateTime2Info.date = dateTime.Subtract(DateTime.MinValue).Days;
_isNull = false;
}

internal void SetToDateTimeOffset(byte[] bytes, int length, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

int length = bytes.Length;
_type = StorageType.DateTimeOffset;
FillInTimeInfo(ref _value._dateTimeOffsetInfo.dateTime2Info.timeInfo, bytes, length - 5, scale); // remaining 5 bytes are for date and offset
_value._dateTimeOffsetInfo.dateTime2Info.date = GetDateFromByteArray(bytes, length - 5); // 3 bytes for date
FillInTimeInfo(ref _value._dateTimeOffsetInfo.dateTime2Info.timeInfo, bytes.Slice(0, length - 5), scale); // remaining 5 bytes are for date and offset
_value._dateTimeOffsetInfo.dateTime2Info.date = GetDateFromByteArray(bytes.Slice(length - 5)); // 3 bytes for date
_value._dateTimeOffsetInfo.offset = (short)(bytes[length - 2] + (bytes[length - 1] << 8)); // 2 bytes for offset (Int16)
_isNull = false;
}

internal void SetToDateTimeOffset(DateTimeOffset dateTimeOffset, byte scale)
{
Debug.Assert(IsEmpty, "setting value a second time?");

_type = StorageType.DateTimeOffset;
DateTime utcDateTime = dateTimeOffset.UtcDateTime; // timeInfo stores the utc datetime of a datatimeoffset
_value._dateTimeOffsetInfo.dateTime2Info.timeInfo.ticks = utcDateTime.TimeOfDay.Ticks;
_value._dateTimeOffsetInfo.dateTime2Info.timeInfo.scale = scale;
_value._dateTimeOffsetInfo.dateTime2Info.date = utcDateTime.Subtract(DateTime.MinValue).Days;
_value._dateTimeOffsetInfo.offset = (short)dateTimeOffset.Offset.TotalMinutes;
_isNull = false;
}

private static void FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int length, byte scale)
private static void FillInTimeInfo(ref TimeInfo timeInfo, ReadOnlySpan<byte> timeBytes, byte scale)
{
int length = timeBytes.Length;
Debug.Assert(3 <= length && length <= 5, "invalid data length for timeInfo: " + length);
Debug.Assert(0 <= scale && scale <= 7, "invalid scale: " + scale);

Expand All @@ -1127,9 +1084,10 @@ private static void FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int
timeInfo.scale = scale;
}

private static int GetDateFromByteArray(byte[] buf, int offset)
private static int GetDateFromByteArray(ReadOnlySpan<byte> buf)
{
return buf[offset] + (buf[offset + 1] << 8) + (buf[offset + 2] << 16);
byte thirdByte = buf[2]; // reordered to optimize JIT generated bounds checks to a single instance, review generated asm before changing
return buf[0] + (buf[1] << 8) + (thirdByte << 16);
}

private void ThrowIfNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1782,7 +1782,7 @@ internal bool TryGetBytesInternalSequential(int i, byte[] buffer, int index, int
{
// Read data (not exceeding the total amount of data available)
int bytesToRead = (int)Math.Min((long)length, _sharedState._columnDataBytesRemaining);
bool result = _stateObj.TryReadByteArray(buffer, index, bytesToRead, out bytesRead);
bool result = _stateObj.TryReadByteArray(buffer.AsSpan(index), bytesToRead, out bytesRead);
_columnDataBytesRead += bytesRead;
_sharedState._columnDataBytesRemaining -= bytesRead;
return result;
Expand Down
36 changes: 18 additions & 18 deletions src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus
byte[] payload = new byte[_physicalStateObj._inBytesPacket];

Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
result = _physicalStateObj.TryReadByteArray(payload, 0, payload.Length);
result = _physicalStateObj.TryReadByteArray(payload, payload.Length);
if (!result) { throw SQL.SynchronousCallMayNotPend(); }

if (payload[0] == 0xaa)
Expand Down Expand Up @@ -2384,7 +2384,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj,
return false;
}
env.newBinValue = new byte[env.newLength];
if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength))
if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength))
{ // read new value with 4 byte length
return false;
}
Expand Down Expand Up @@ -2476,7 +2476,7 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state
}
env.newLength = byteLength;
env.newBinValue = new byte[env.newLength];
if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength))
if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength))
{
return false;
}
Expand All @@ -2486,7 +2486,7 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state
}
env.oldLength = byteLength;
env.oldBinValue = new byte[env.oldLength];
if (!stateObj.TryReadByteArray(env.oldBinValue, 0, env.oldLength))
if (!stateObj.TryReadByteArray(env.oldBinValue, env.oldLength))
{
return false;
}
Expand Down Expand Up @@ -2739,7 +2739,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj)
byte[] data = new byte[dataLen];
if (dataLen > 0)
{
if (!stateObj.TryReadByteArray(data, 0, checked((int)dataLen)))
if (!stateObj.TryReadByteArray(data, checked((int)dataLen)))
{
return false;
}
Expand Down Expand Up @@ -2844,7 +2844,7 @@ private bool TryProcessSessionState(TdsParserStateObject stateObj, int length, S
}
if (buffer != null)
{
if (!stateObj.TryReadByteArray(buffer, 0, stateLen))
if (!stateObj.TryReadByteArray(buffer, stateLen))
{
return false;
}
Expand Down Expand Up @@ -2882,7 +2882,7 @@ private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck s
}

byte[] b = new byte[TdsEnums.VERSION_SIZE];
if (!stateObj.TryReadByteArray(b, 0, b.Length))
if (!stateObj.TryReadByteArray(b, b.Length))
{
return false;
}
Expand Down Expand Up @@ -4505,7 +4505,7 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T
{
//Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
b = new byte[length];
if (!stateObj.TryReadByteArray(b, 0, length))
if (!stateObj.TryReadByteArray(b, length))
{
return false;
}
Expand Down Expand Up @@ -4565,33 +4565,33 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T

private bool TryReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj)
{
byte[] datetimeBuffer = new byte[length];
Span<byte> datetimeBuffer = ((uint)length <= 16) ? stackalloc byte[16] : new byte[length];

if (!stateObj.TryReadByteArray(datetimeBuffer, 0, length))
if (!stateObj.TryReadByteArray(datetimeBuffer, length))
{
return false;
}

ReadOnlySpan<byte> dateTimeData = datetimeBuffer.Slice(0, length);
switch (tdsType)
{
case TdsEnums.SQLDATE:
Debug.Assert(length == 3, "invalid length for date type!");
value.SetToDate(datetimeBuffer);
value.SetToDate(dateTimeData);
break;

case TdsEnums.SQLTIME:
Debug.Assert(3 <= length && length <= 5, "invalid length for time type!");
value.SetToTime(datetimeBuffer, length, scale);
value.SetToTime(dateTimeData, scale);
break;

case TdsEnums.SQLDATETIME2:
Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!");
value.SetToDateTime2(datetimeBuffer, length, scale);
value.SetToDateTime2(dateTimeData, scale);
break;

case TdsEnums.SQLDATETIMEOFFSET:
Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!");
value.SetToDateTimeOffset(datetimeBuffer, length, scale);
value.SetToDateTimeOffset(dateTimeData, scale);
break;

default:
Expand Down Expand Up @@ -4785,7 +4785,7 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length,

byte[] b = new byte[length];

if (!stateObj.TryReadByteArray(b, 0, length))
if (!stateObj.TryReadByteArray(b, length))
{
return false;
}
Expand All @@ -4802,7 +4802,7 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length,
// Note: Better not come here with plp data!!
Debug.Assert(length <= TdsEnums.MAXSIZE);
byte[] b = new byte[length];
if (!stateObj.TryReadByteArray(b, 0, length))
if (!stateObj.TryReadByteArray(b, length))
{
return false;
}
Expand Down Expand Up @@ -6565,7 +6565,7 @@ private void ProcessSSPI(int receivedLength)

// read SSPI data received from server
Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
bool result = _physicalStateObj.TryReadByteArray(receivedBuff, 0, receivedLength);
bool result = _physicalStateObj.TryReadByteArray(receivedBuff, receivedLength);
if (!result) { throw SQL.SynchronousCallMayNotPend(); }

// allocate send buffer and initialize length
Expand Down
Loading