diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index da5a2d1062..a8e636aeb7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -283,6 +283,9 @@ Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs + + Microsoft\Data\SqlClient\SqlBuffer.cs + Microsoft\Data\SqlClient\SqlAuthenticationToken.cs @@ -616,7 +619,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 2580fab991..09af683885 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -350,6 +350,9 @@ Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs + + Microsoft\Data\SqlClient\SqlBuffer.cs + Microsoft\Data\SqlClient\SqlAuthenticationToken.cs @@ -591,7 +594,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs deleted file mode 100644 index f2d332c95f..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ /dev/null @@ -1,1400 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; -using Microsoft.Data.SqlTypes; - -namespace Microsoft.Data.SqlClient -{ - internal sealed partial class SqlBuffer - { - internal enum StorageType - { - Empty = 0, - Boolean, - Byte, - DateTime, - Decimal, - Double, - Int16, - Int32, - Int64, - Guid, - Money, - Single, - String, - SqlBinary, - SqlCachedBuffer, - SqlGuid, - SqlXml, - Date, - DateTime2, - DateTimeOffset, - Time, - } - - internal struct DateTimeInfo - { - // This is used to store DateTime - internal int _daypart; - internal int _timepart; - } - - internal struct NumericInfo - { - // This is used to store Decimal data - internal int _data1; - internal int _data2; - internal int _data3; - internal int _data4; - internal byte _precision; - internal byte _scale; - internal bool _positive; - } - - internal struct TimeInfo - { - internal long _ticks; - internal byte _scale; - } - - internal struct DateTime2Info - { - internal int _date; - internal TimeInfo _timeInfo; - } - - internal struct DateTimeOffsetInfo - { - internal DateTime2Info _dateTime2Info; - internal short _offset; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct Storage - { - [FieldOffset(0)] - internal bool _boolean; - [FieldOffset(0)] - internal byte _byte; - [FieldOffset(0)] - internal DateTimeInfo _dateTimeInfo; - [FieldOffset(0)] - internal double _double; - [FieldOffset(0)] - internal NumericInfo _numericInfo; - [FieldOffset(0)] - internal short _int16; - [FieldOffset(0)] - internal int _int32; - [FieldOffset(0)] - internal long _int64; // also used to store Money, UtcDateTime, Date , and Time - [FieldOffset(0)] - internal Guid _guid; - [FieldOffset(0)] - internal float _single; - [FieldOffset(0)] - internal TimeInfo _timeInfo; - [FieldOffset(0)] - internal DateTime2Info _dateTime2Info; - [FieldOffset(0)] - internal DateTimeOffsetInfo _dateTimeOffsetInfo; - } - - private bool _isNull; - private StorageType _type; - private Storage _value; - private object _object; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml - - internal SqlBuffer() - { - } - - private SqlBuffer(SqlBuffer value) - { // Clone - // value types - _isNull = value._isNull; - _type = value._type; - // ref types - should also be read only unless at some point we allow this data - // to be mutable, then we will need to copy - _value = value._value; - _object = value._object; - } - - internal bool IsEmpty => _type == StorageType.Empty; - - internal bool IsNull => _isNull; - - internal StorageType VariantInternalStorageType => _type; - - internal bool Boolean - { - get - { - ThrowIfNull(); - - if (StorageType.Boolean == _type) - { - return _value._boolean; - } - return (bool)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._boolean = value; - _type = StorageType.Boolean; - _isNull = false; - } - } - - internal byte Byte - { - get - { - ThrowIfNull(); - - if (StorageType.Byte == _type) - { - return _value._byte; - } - return (byte)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._byte = value; - _type = StorageType.Byte; - _isNull = false; - } - } - - internal byte[] ByteArray - { - get - { - ThrowIfNull(); - return SqlBinary.Value; - } - } - - internal DateTime DateTime - { - get - { - ThrowIfNull(); - - if (StorageType.Date == _type) - { - return DateTime.MinValue.AddDays(_value._int32); - } - if (StorageType.DateTime2 == _type) - { - return new DateTime(GetTicksFromDateTime2Info(_value._dateTime2Info)); - } - if (StorageType.DateTime == _type) - { - return SqlTypeWorkarounds.SqlDateTimeToDateTime(_value._dateTimeInfo._daypart, _value._dateTimeInfo._timepart); - } - return (DateTime)Value; // anything else we haven't thought of goes through boxing. - } - } - - #region Decimal - internal decimal Decimal - { - get - { - ThrowIfNull(); - - if (StorageType.Decimal == _type) - { - if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28) - { - // Only removing trailing zeros from a decimal part won't hit its value! - if (_value._numericInfo._scale > 0) - { - int zeroCnt = FindTrailingZerosAndPrec((uint)_value._numericInfo._data1, (uint)_value._numericInfo._data2, - (uint)_value._numericInfo._data3, (uint)_value._numericInfo._data4, - _value._numericInfo._scale, out int precision); - - int minScale = _value._numericInfo._scale - zeroCnt; // minimum possible sacle after removing the trailing zeros. - - if (zeroCnt > 0 && minScale <= 28 && precision <= 29) - { - SqlDecimal sqlValue = new(_value._numericInfo._precision, _value._numericInfo._scale, _value._numericInfo._positive, - _value._numericInfo._data1, _value._numericInfo._data2, - _value._numericInfo._data3, _value._numericInfo._data4); - - int integral = precision - minScale; - int newPrec = 29; - - if (integral != 1 && precision != 29) - { - newPrec = 28; - } - - try - { - // Precision could be 28 or 29 - // ex: (precision == 29 && scale == 28) - // valid: (+/-)7.1234567890123456789012345678 - // invalid: (+/-)8.1234567890123456789012345678 - return SqlDecimal.ConvertToPrecScale(sqlValue, newPrec, newPrec - integral).Value; - } - catch (OverflowException) - { - throw new OverflowException(SQLResource.ConversionOverflowMessage); - } - } - } - throw new OverflowException(SQLResource.ConversionOverflowMessage); - } - return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale); - } - if (StorageType.Money == _type) - { - long l = _value._int64; - bool isNegative = false; - if (l < 0) - { - isNegative = true; - l = -l; - } - return new decimal((int)(l & 0xffffffff), (int)(l >> 32), 0, isNegative, 4); - } - return (decimal)Value; // anything else we haven't thought of goes through boxing. - } - } - - /// - /// Returns number of trailing zeros using the supplied parameters. - /// - /// An 32-bit unsigned integer which will be combined with data2, data3, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data3, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data2, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data2, and data3 - /// The number of decimal places - /// OUT |The number of digits without trailing zeros - /// Number of trailing zeros - private static int FindTrailingZerosAndPrec(uint data1, uint data2, uint data3, uint data4, byte scale, out int valuablePrecision) - { - // Make local copy of data to avoid modifying input. - Span rgulNumeric = stackalloc uint[4] { data1, data2, data3, data4 }; - int zeroCnt = 0; //Number of trailing zero digits - int precCnt = 0; //Valuable precision - uint uiRem = 0; //Remainder of a division by 10 - int len = 4; // Max possible items - - //Retrieve each digit from the lowest significant digit - while (len > 1 || rgulNumeric[0] != 0) - { - SqlDecimalDivBy(rgulNumeric, ref len, 10, out uiRem); - if (uiRem == 0 && precCnt == 0) - { - zeroCnt++; - } - else - { - precCnt++; - } - } - - if (uiRem == 0) - { - zeroCnt = scale; - } - - // if scale of the number has not been reached, pad remaining number with zeros. - if (zeroCnt + precCnt <= scale) - { - precCnt = scale - zeroCnt + 1; - } - valuablePrecision = precCnt; - return zeroCnt; - } - - /// - /// Multi-precision one super-digit divide in place. - /// U = U / D, - /// R = U % D - /// (Length of U can decrease) - /// - /// InOut | U - /// InOut | Number of items with non-zero value in U between 1 to 4 - /// In | D - /// Out | R - private static void SqlDecimalDivBy(Span data, ref int len, uint divisor, out uint remainder) - { - uint uiCarry = 0; - ulong ulAccum; - ulong ulDivisor = (ulong)divisor; - int iLen = len; - - while (iLen > 0) - { - iLen--; - ulAccum = (((ulong)uiCarry) << 32) + (ulong)(data[iLen]); - data[iLen] = (uint)(ulAccum / ulDivisor); - uiCarry = (uint)(ulAccum - (ulong)data[iLen] * ulDivisor); // (ULONG) (ulAccum % divisor) - } - remainder = uiCarry; - - // Normalize multi-precision number - remove leading zeroes - while (len > 1 && data[len - 1] == 0) - { len--; } - } - #endregion - - internal double Double - { - get - { - ThrowIfNull(); - - if (StorageType.Double == _type) - { - return _value._double; - } - return (double)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._double = value; - _type = StorageType.Double; - _isNull = false; - } - } - - internal Guid Guid - { - get - { - ThrowIfNull(); - if (StorageType.Guid == _type) - { - return _value._guid; - } - else if (StorageType.SqlGuid == _type) - { - return ((SqlGuid)_object).Value; - } - return (Guid)Value; - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _type = StorageType.Guid; - _value._guid = value; - _isNull = false; - } - } - - internal short Int16 - { - get - { - ThrowIfNull(); - - if (StorageType.Int16 == _type) - { - return _value._int16; - } - return (short)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int16 = value; - _type = StorageType.Int16; - _isNull = false; - } - } - - internal int Int32 - { - get - { - ThrowIfNull(); - - if (StorageType.Int32 == _type) - { - return _value._int32; - } - return (int)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int32 = value; - _type = StorageType.Int32; - _isNull = false; - } - } - - internal long Int64 - { - get - { - ThrowIfNull(); - - if (StorageType.Int64 == _type) - { - return _value._int64; - } - return (long)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int64 = value; - _type = StorageType.Int64; - _isNull = false; - } - } - - internal float Single - { - get - { - ThrowIfNull(); - - if (StorageType.Single == _type) - { - return _value._single; - } - return (float)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._single = value; - _type = StorageType.Single; - _isNull = false; - } - } - - internal string String - { - get - { - ThrowIfNull(); - - if (StorageType.String == _type) - { - return (string)_object; - } - else if (StorageType.SqlCachedBuffer == _type) - { - return ((SqlCachedBuffer)(_object)).ToString(); - } - return (string)Value; // anything else we haven't thought of goes through boxing. - } - } - - // use static list of format strings indexed by scale for perf - private static readonly string[] s_sql2008DateTimeOffsetFormatByScale = new string[] { - "yyyy-MM-dd HH:mm:ss zzz", - "yyyy-MM-dd HH:mm:ss.f zzz", - "yyyy-MM-dd HH:mm:ss.ff zzz", - "yyyy-MM-dd HH:mm:ss.fff zzz", - "yyyy-MM-dd HH:mm:ss.ffff zzz", - "yyyy-MM-dd HH:mm:ss.fffff zzz", - "yyyy-MM-dd HH:mm:ss.ffffff zzz", - "yyyy-MM-dd HH:mm:ss.fffffff zzz", - }; - - private static readonly string[] s_sql2008DateTime2FormatByScale = new string[] { - "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-dd HH:mm:ss.f", - "yyyy-MM-dd HH:mm:ss.ff", - "yyyy-MM-dd HH:mm:ss.fff", - "yyyy-MM-dd HH:mm:ss.ffff", - "yyyy-MM-dd HH:mm:ss.fffff", - "yyyy-MM-dd HH:mm:ss.ffffff", - "yyyy-MM-dd HH:mm:ss.fffffff", - }; - - private static readonly string[] s_sql2008TimeFormatByScale = new string[] { - "HH:mm:ss", - "HH:mm:ss.f", - "HH:mm:ss.ff", - "HH:mm:ss.fff", - "HH:mm:ss.ffff", - "HH:mm:ss.fffff", - "HH:mm:ss.ffffff", - "HH:mm:ss.fffffff", - }; - - internal string Sql2008DateTimeString - { - get - { - ThrowIfNull(); - - if (StorageType.Date == _type) - { - return DateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.Time == _type) - { - byte scale = _value._timeInfo._scale; - return new DateTime(_value._timeInfo._ticks).ToString(s_sql2008TimeFormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.DateTime2 == _type) - { - byte scale = _value._dateTime2Info._timeInfo._scale; - return DateTime.ToString(s_sql2008DateTime2FormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.DateTimeOffset == _type) - { - DateTimeOffset dto = DateTimeOffset; - byte scale = _value._dateTimeOffsetInfo._dateTime2Info._timeInfo._scale; - return dto.ToString(s_sql2008DateTimeOffsetFormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - return (string)Value; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlString Sql2008DateTimeSqlString - { - get - { - if (StorageType.Date == _type || - StorageType.Time == _type || - StorageType.DateTime2 == _type || - StorageType.DateTimeOffset == _type) - { - if (IsNull) - { - return SqlString.Null; - } - return new SqlString(Sql2008DateTimeString); - } - return (SqlString)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal TimeSpan Time - { - get - { - ThrowIfNull(); - - if (StorageType.Time == _type) - { - return new TimeSpan(_value._timeInfo._ticks); - } - - return (TimeSpan)Value; // anything else we haven't thought of goes through boxing. - } - } - - internal DateTimeOffset DateTimeOffset - { - get - { - ThrowIfNull(); - - if (StorageType.DateTimeOffset == _type) - { - TimeSpan offset = new TimeSpan(0, _value._dateTimeOffsetInfo._offset, 0); - // datetime part presents time in UTC - return new DateTimeOffset(GetTicksFromDateTime2Info(_value._dateTimeOffsetInfo._dateTime2Info) + offset.Ticks, offset); - } - - return (DateTimeOffset)Value; // anything else we haven't thought of goes through boxing. - } - } - - private static long GetTicksFromDateTime2Info(DateTime2Info dateTime2Info) - { - return (dateTime2Info._date * TimeSpan.TicksPerDay + dateTime2Info._timeInfo._ticks); - } - - internal SqlBinary SqlBinary - { - get - { - if (StorageType.SqlBinary == _type) - { - return (SqlBinary)_object; - } - return (SqlBinary)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlBinary; - _isNull = value.IsNull; - } - } - - internal SqlBoolean SqlBoolean - { - get - { - if (StorageType.Boolean == _type) - { - if (IsNull) - { - return SqlBoolean.Null; - } - return new SqlBoolean(_value._boolean); - } - return (SqlBoolean)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlByte SqlByte - { - get - { - if (StorageType.Byte == _type) - { - if (IsNull) - { - return SqlByte.Null; - } - return new SqlByte(_value._byte); - } - return (SqlByte)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlCachedBuffer SqlCachedBuffer - { - get - { - if (StorageType.SqlCachedBuffer == _type) - { - if (IsNull) - { - return SqlCachedBuffer.Null; - } - return (SqlCachedBuffer)_object; - } - return (SqlCachedBuffer)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlCachedBuffer; - _isNull = value.IsNull; - } - } - - internal SqlXml SqlXml - { - get - { - if (StorageType.SqlXml == _type) - { - if (IsNull) - { - return SqlXml.Null; - } - return (SqlXml)_object; - } - return (SqlXml)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlXml; - _isNull = value.IsNull; - } - } - - internal SqlDateTime SqlDateTime - { - get - { - if (StorageType.DateTime == _type) - { - if (IsNull) - { - return SqlDateTime.Null; - } - return new SqlDateTime(_value._dateTimeInfo._daypart, _value._dateTimeInfo._timepart); - } - return (SqlDateTime)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlDecimal SqlDecimal - { - get - { - if (StorageType.Decimal == _type) - { - if (IsNull) - { - return SqlDecimal.Null; - } - return new SqlDecimal(_value._numericInfo._precision, - _value._numericInfo._scale, - _value._numericInfo._positive, - _value._numericInfo._data1, - _value._numericInfo._data2, - _value._numericInfo._data3, - _value._numericInfo._data4 - ); - } - return (SqlDecimal)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlDouble SqlDouble - { - get - { - if (StorageType.Double == _type) - { - if (IsNull) - { - return SqlDouble.Null; - } - return new SqlDouble(_value._double); - } - return (SqlDouble)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlGuid SqlGuid - { - get - { - if (StorageType.Guid == _type) - { - return new SqlGuid(_value._guid); - } - else if (StorageType.SqlGuid == _type) - { - return IsNull ? SqlGuid.Null : (SqlGuid)_object; - } - return (SqlGuid)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlGuid; - _isNull = value.IsNull; - } - } - - internal SqlInt16 SqlInt16 - { - get - { - if (StorageType.Int16 == _type) - { - if (IsNull) - { - return SqlInt16.Null; - } - return new SqlInt16(_value._int16); - } - return (SqlInt16)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlInt32 SqlInt32 - { - get - { - if (StorageType.Int32 == _type) - { - if (IsNull) - { - return SqlInt32.Null; - } - return new SqlInt32(_value._int32); - } - return (SqlInt32)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlInt64 SqlInt64 - { - get - { - if (StorageType.Int64 == _type) - { - if (IsNull) - { - return SqlInt64.Null; - } - return new SqlInt64(_value._int64); - } - return (SqlInt64)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlMoney SqlMoney - { - get - { - if (StorageType.Money == _type) - { - if (IsNull) - { - return SqlMoney.Null; - } - return SqlTypeWorkarounds.SqlMoneyCtor(_value._int64, 1/*ignored*/); - } - return (SqlMoney)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlSingle SqlSingle - { - get - { - if (StorageType.Single == _type) - { - if (IsNull) - { - return SqlSingle.Null; - } - return new SqlSingle(_value._single); - } - return (SqlSingle)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlString SqlString - { - get - { - if (StorageType.String == _type) - { - if (IsNull) - { - return SqlString.Null; - } - return new SqlString((string)_object); - } - else if (StorageType.SqlCachedBuffer == _type) - { - SqlCachedBuffer data = (SqlCachedBuffer)(_object); - if (data.IsNull) - { - return SqlString.Null; - } - return data.ToSqlString(); - } - return (SqlString)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal object SqlValue - { - get - { - switch (_type) - { - case StorageType.Empty: - return DBNull.Value; - case StorageType.Boolean: - return SqlBoolean; - case StorageType.Byte: - return SqlByte; - case StorageType.DateTime: - return SqlDateTime; - case StorageType.Decimal: - return SqlDecimal; - case StorageType.Double: - return SqlDouble; - case StorageType.Int16: - return SqlInt16; - case StorageType.Int32: - return SqlInt32; - case StorageType.Int64: - return SqlInt64; - case StorageType.Guid: - return SqlGuid; - case StorageType.Money: - return SqlMoney; - case StorageType.Single: - return SqlSingle; - case StorageType.String: - return SqlString; - - case StorageType.SqlCachedBuffer: - { - SqlCachedBuffer data = (SqlCachedBuffer)(_object); - if (data.IsNull) - { - return SqlXml.Null; - } - return data.ToSqlXml(); - } - - case StorageType.SqlBinary: - case StorageType.SqlGuid: - return _object; - - case StorageType.SqlXml: - if (_isNull) - { - return SqlXml.Null; - } - Debug.Assert(null != _object); - return (SqlXml)_object; - - case StorageType.Date: - case StorageType.DateTime2: - if (_isNull) - { - return DBNull.Value; - } - return DateTime; - - case StorageType.DateTimeOffset: - if (_isNull) - { - return DBNull.Value; - } - return DateTimeOffset; - - case StorageType.Time: - if (_isNull) - { - return DBNull.Value; - } - return Time; - } - return null; // need to return the value as an object of some SQL type - } - } - - - // these variables store pre-boxed bool values to be used when returning a boolean - // in a object typed location, if these are not used a new value is boxed each time - // one is needed which leads to a lot of garbage which needs to be collected - private static readonly object s_cachedTrueObject = true; - private static readonly object s_cachedFalseObject = false; - - internal object Value - { - get - { - if (IsNull) - { - return DBNull.Value; - } - switch (_type) - { - case StorageType.Empty: - return DBNull.Value; - case StorageType.Boolean: - return Boolean ? s_cachedTrueObject : s_cachedFalseObject; // return pre-boxed values for perf - case StorageType.Byte: - return Byte; - case StorageType.DateTime: - return DateTime; - case StorageType.Decimal: - return Decimal; - case StorageType.Double: - return Double; - case StorageType.Int16: - return Int16; - case StorageType.Int32: - return Int32; - case StorageType.Int64: - return Int64; - case StorageType.Guid: - return Guid; - case StorageType.Money: - return Decimal; - case StorageType.Single: - return Single; - case StorageType.String: - return String; - case StorageType.SqlBinary: - return ByteArray; - case StorageType.SqlCachedBuffer: - { - // If we have a CachedBuffer, it's because it's an XMLTYPE column - // and we have to return a string when they're asking for the CLS - // value of the column. - return ((SqlCachedBuffer)(_object)).ToString(); - } - case StorageType.SqlGuid: - return Guid; - case StorageType.SqlXml: - { - // XMLTYPE columns must be returned as string when asking for the CLS value - SqlXml data = (SqlXml)_object; - string s = data.Value; - return s; - } - case StorageType.Date: - return DateTime; - case StorageType.DateTime2: - return DateTime; - case StorageType.DateTimeOffset: - return DateTimeOffset; - case StorageType.Time: - return Time; - } - return null; // need to return the value as an object of some CLS type - } - } - - internal Type GetTypeFromStorageType(bool isSqlType) - { - if (isSqlType) - { - switch (_type) - { - case StorageType.Empty: - return null; - case StorageType.Boolean: - return typeof(SqlBoolean); - case StorageType.Byte: - return typeof(SqlByte); - case StorageType.DateTime: - return typeof(SqlDateTime); - case StorageType.Decimal: - return typeof(SqlDecimal); - case StorageType.Double: - return typeof(SqlDouble); - case StorageType.Int16: - return typeof(SqlInt16); - case StorageType.Int32: - return typeof(SqlInt32); - case StorageType.Int64: - return typeof(SqlInt64); - case StorageType.Guid: - return typeof(SqlGuid); - case StorageType.Money: - return typeof(SqlMoney); - case StorageType.Single: - return typeof(SqlSingle); - case StorageType.String: - return typeof(SqlString); - case StorageType.SqlCachedBuffer: - return typeof(SqlString); - case StorageType.SqlBinary: - return typeof(object); - case StorageType.SqlGuid: - return typeof(SqlGuid); - case StorageType.SqlXml: - return typeof(SqlXml); - // Date DateTime2 and DateTimeOffset have no direct Sql type to contain them - } - } - else - { //Is CLR Type - switch (_type) - { - case StorageType.Empty: - return null; - case StorageType.Boolean: - return typeof(bool); - case StorageType.Byte: - return typeof(byte); - case StorageType.DateTime: - return typeof(DateTime); - case StorageType.Decimal: - return typeof(decimal); - case StorageType.Double: - return typeof(double); - case StorageType.Int16: - return typeof(short); - case StorageType.Int32: - return typeof(int); - case StorageType.Int64: - return typeof(long); - case StorageType.Guid: - return typeof(Guid); - case StorageType.Money: - return typeof(decimal); - case StorageType.Single: - return typeof(float); - case StorageType.String: - return typeof(string); - case StorageType.SqlBinary: - return typeof(byte[]); - case StorageType.SqlCachedBuffer: - return typeof(string); - case StorageType.SqlGuid: - return typeof(Guid); - case StorageType.SqlXml: - return typeof(string); - } - } - - return null; // need to return the value as an object of some CLS type - } - - internal static SqlBuffer[] CreateBufferArray(int length) - { - SqlBuffer[] buffers = new SqlBuffer[length]; - for (int i = 0; i < buffers.Length; ++i) - { - buffers[i] = new SqlBuffer(); - } - return buffers; - } - - internal static SqlBuffer[] CloneBufferArray(SqlBuffer[] values) - { - SqlBuffer[] copy = new SqlBuffer[values.Length]; - for (int i = 0; i < values.Length; i++) - { - copy[i] = new SqlBuffer(values[i]); - } - return copy; - } - - internal static void Clear(SqlBuffer[] values) - { - if (null != values) - { - for (int i = 0; i < values.Length; ++i) - { - values[i].Clear(); - } - } - } - - internal void Clear() - { - _isNull = false; - _type = StorageType.Empty; - _object = null; - } - - internal void SetToDateTime(int daypart, int timepart) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._dateTimeInfo._daypart = daypart; - _value._dateTimeInfo._timepart = timepart; - _type = StorageType.DateTime; - _isNull = false; - } - - internal void SetToDecimal(byte precision, byte scale, bool positive, int[] bits) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._numericInfo._precision = precision; - _value._numericInfo._scale = scale; - _value._numericInfo._positive = positive; - _value._numericInfo._data1 = bits[0]; - _value._numericInfo._data2 = bits[1]; - _value._numericInfo._data3 = bits[2]; - _value._numericInfo._data4 = bits[3]; - _type = StorageType.Decimal; - _isNull = false; - } - - internal void SetToMoney(long value) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int64 = value; - _type = StorageType.Money; - _isNull = false; - } - - internal void SetToNullOfType(StorageType storageType) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _type = storageType; - _isNull = true; - _object = null; - } - - internal void SetToString(string value) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.String; - _isNull = false; - } - - internal void SetToDate(byte[] bytes) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Date; - _value._int32 = GetDateFromByteArray(bytes, 0); - _isNull = false; - } - - internal void SetToDate(DateTime date) - { - 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, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Time; - FillInTimeInfo(ref _value._timeInfo, bytes, length, scale, denormalizedScale); - _isNull = false; - } - - internal void SetToTime(TimeSpan timeSpan, byte scale) - { - 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, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTime2; - FillInTimeInfo(ref _value._dateTime2Info._timeInfo, bytes, length - 3, scale, denormalizedScale); // remaining 3 bytes is for date - _value._dateTime2Info._date = GetDateFromByteArray(bytes, length - 3); // 3 bytes for date - _isNull = false; - } - - internal void SetToDateTime2(DateTime dateTime, 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, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTimeOffset; - FillInTimeInfo(ref _value._dateTimeOffsetInfo._dateTime2Info._timeInfo, bytes, length - 5, scale, denormalizedScale); // remaining 5 bytes are for date and offset - _value._dateTimeOffsetInfo._dateTime2Info._date = GetDateFromByteArray(bytes, 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, byte denormalizedScale) - { - Debug.Assert(3 <= length && length <= 5, "invalid data length for timeInfo: " + length); - Debug.Assert(0 <= scale && scale <= 7, "invalid scale: " + scale); - Debug.Assert(0 <= denormalizedScale && denormalizedScale <= 7, "invalid denormalized scale: " + denormalizedScale); - - long tickUnits = timeBytes[0] + ((long)timeBytes[1] << 8) + ((long)timeBytes[2] << 16); - if (length > 3) - { - tickUnits += ((long)timeBytes[3] << 24); - } - if (length > 4) - { - tickUnits += ((long)timeBytes[4] << 32); - } - timeInfo._ticks = tickUnits * TdsEnums.TICKS_FROM_SCALE[scale]; - - // Once the deserialization has been completed using the value scale, we need to set the actual denormalized scale, - // coming from the data type, on the original result, so that it has the proper scale setting. - // This only applies for values that got serialized/deserialized for encryption. Otherwise, both scales should be equal. - timeInfo._scale = denormalizedScale; - } - - private static int GetDateFromByteArray(byte[] buf, int offset) - { - return buf[offset] + (buf[offset + 1] << 8) + (buf[offset + 2] << 16); - } - - private void ThrowIfNull() - { - if (IsNull) - { - throw new SqlNullValueException(); - } - } - // [Field]As method explanation: - // these methods are used to bridge generic to non-generic access to value type fields on the storage struct - // where typeof(T) == typeof(field) - // 1) RyuJIT will recognize the pattern of (T)(object)T as being redundant and eliminate - // the T and object casts leaving T, so while this looks like it will put every value type instance in a box the - // generated assembly will be short and direct - // 2) another jit may not recognize the pattern and should emit the code as seen. this will box and then unbox the - // value type which is no worse than the mechanism that this code replaces - // where typeof(T) != typeof(field) - // the jit will emit all the cast operations as written. this will put the value into a box and then attempt to - // cast it, because it is an object no conversions are used and this will generate the desired InvalidCastException - // for example users cannot widen a short to an int preserving external expectations - - internal T ByteAs() - { - ThrowIfNull(); - return (T)(object)_value._byte; - } - - internal T BooleanAs() - { - ThrowIfNull(); - return (T)(object)_value._boolean; - } - - internal T Int32As() - { - ThrowIfNull(); - return (T)(object)_value._int32; - } - - internal T Int16As() - { - ThrowIfNull(); - return (T)(object)_value._int16; - } - - internal T Int64As() - { - ThrowIfNull(); - return (T)(object)_value._int64; - } - - internal T DoubleAs() - { - ThrowIfNull(); - return (T)(object)_value._double; - } - - internal T SingleAs() - { - ThrowIfNull(); - return (T)(object)_value._single; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs new file mode 100644 index 0000000000..2fc89dfde1 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace Microsoft.Data.SqlClient +{ + internal sealed partial class SqlBuffer + { + internal void SetToDate(DateTime date) + { + Debug.Assert(IsEmpty, "setting value a second time?"); + + _type = StorageType.Date; + _value._int32 = date.Subtract(DateTime.MinValue).Days; + _isNull = false; + } + + internal void SetToDateTime2(DateTime dateTime, 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; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 7681520031..09061277e0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -2199,7 +2199,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(start: index), bytesToRead, out bytesRead); _columnDataBytesRead += bytesRead; _sharedState._columnDataBytesRemaining -= bytesRead; return result; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index e69df7dd19..45cdba3032 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1162,7 +1162,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod 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(); } @@ -3188,7 +3188,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; } @@ -3283,7 +3283,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; } @@ -3293,7 +3293,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; } @@ -3593,7 +3593,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; } @@ -3981,7 +3981,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; } @@ -4018,14 +4018,14 @@ private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck s return false; } - byte[] b = new byte[TdsEnums.VERSION_SIZE]; - if (!stateObj.TryReadByteArray(b, 0, b.Length)) + Span b = stackalloc byte[TdsEnums.VERSION_SIZE]; + if (!stateObj.TryReadByteArray(b, b.Length)) { return false; } - a.tdsVersion = (UInt32)((((((b[0] << 8) | b[1]) << 8) | b[2]) << 8) | b[3]); // bytes are in motorola order (high byte first) - UInt32 majorMinor = a.tdsVersion & 0xff00ffff; - UInt32 increment = (a.tdsVersion >> 16) & 0xff; + a.tdsVersion = (uint)((((((b[0] << 8) | b[1]) << 8) | b[2]) << 8) | b[3]); // bytes are in motorola order (high byte first) + uint majorMinor = a.tdsVersion & 0xff00ffff; + uint increment = (a.tdsVersion >> 16) & 0xff; // Server responds: // 0x07000000 -> 7.0 // Notice server response format is different for bwd compat @@ -4168,7 +4168,7 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, // read the rest of the token byte[] tokenData = new byte[tokenLen]; int totalRead = 0; - bool successfulRead = stateObj.TryReadByteArray(tokenData, 0, tokenLen, out totalRead); + bool successfulRead = stateObj.TryReadByteArray(tokenData, tokenLen, out totalRead); if (SqlClientEventSource.Log.IsAdvancedTraceOn()) { SqlClientEventSource.Log.AdvancedTraceEvent(" Read rest of FEDAUTHINFO token stream: {0}", BitConverter.ToString(tokenData, 0, totalRead)); @@ -5173,7 +5173,7 @@ internal bool TryReadCipherInfoEntry(TdsParserStateObject stateObj, out SqlTceCi // Read the key MD Version byte[] keyMDVersion = new byte[8]; - if (!stateObj.TryReadByteArray(keyMDVersion, 0, 8)) + if (!stateObj.TryReadByteArray(keyMDVersion, 8)) { return false; } @@ -5206,7 +5206,7 @@ internal bool TryReadCipherInfoEntry(TdsParserStateObject stateObj, out SqlTceCi encryptedCek = new byte[length]; // Read the actual encrypted CEK - if (!stateObj.TryReadByteArray(encryptedCek, 0, length)) + if (!stateObj.TryReadByteArray(encryptedCek, length)) { return false; } @@ -6663,19 +6663,19 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt case TdsEnums.SQLTIME: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 5, "invalid length for time type!"); - value.SetToTime(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToTime(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; case TdsEnums.SQLDATETIME2: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 8, "invalid length for datetime2 type!"); - value.SetToDateTime2(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToDateTime2(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; case TdsEnums.SQLDATETIMEOFFSET: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 10, "invalid length for datetimeoffset type!"); - value.SetToDateTimeOffset(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToDateTimeOffset(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; default: @@ -6736,8 +6736,7 @@ internal bool TryReadSqlValue(SqlBuffer value, { // If we are given -1 for length, then we read the entire value, // otherwise only the requested amount, usually first chunk. - int ignored; - if (!stateObj.TryReadPlpBytes(ref b, 0, length, out ignored)) + if (!stateObj.TryReadPlpBytes(ref b, 0, length, out _)) { return false; } @@ -6746,7 +6745,7 @@ internal bool TryReadSqlValue(SqlBuffer value, { //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; } @@ -6830,33 +6829,33 @@ internal bool TryReadSqlValue(SqlBuffer value, private bool TryReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) { - byte[] datetimeBuffer = new byte[length]; + Span datetimeBuffer = ((uint)length <= 16) ? stackalloc byte[16] : new byte[length]; - if (!stateObj.TryReadByteArray(datetimeBuffer, 0, length)) + if (!stateObj.TryReadByteArray(datetimeBuffer, length)) { return false; } - + ReadOnlySpan 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, scale); + value.SetToTime(dateTimeData, scale, scale); break; case TdsEnums.SQLDATETIME2: Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!"); - value.SetToDateTime2(datetimeBuffer, length, scale, scale); + value.SetToDateTime2(dateTimeData, scale, scale); break; case TdsEnums.SQLDATETIMEOFFSET: Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!"); - value.SetToDateTimeOffset(datetimeBuffer, length, scale, scale); + value.SetToDateTimeOffset(dateTimeData, scale, scale); break; default: @@ -7053,7 +7052,7 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, { b = new byte[GUID_SIZE]; } - if (!stateObj.TryReadByteArray(b, 0, length)) + if (!stateObj.TryReadByteArray(b, length)) { return false; } @@ -7071,7 +7070,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; } @@ -9302,7 +9301,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(); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index b22faacb58..1ede8f6baf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -580,7 +580,7 @@ internal bool TryInitialize(TdsParserStateObject stateObj, int columnsCount) } // read the null bitmap compression information from TDS - if (!stateObj.TryReadByteArray(_nullBitmap, 0, _nullBitmap.Length)) + if (!stateObj.TryReadByteArray(_nullBitmap, _nullBitmap.Length)) { return false; } @@ -1389,15 +1389,14 @@ internal bool TryPeekByte(out byte value) // Takes a byte array, an offset, and a len and fills the array from the offset to len number of // bytes from the in buffer. - public bool TryReadByteArray(byte[] buff, int offset, int len) + public bool TryReadByteArray(Span buff, int len) { - int ignored; - return TryReadByteArray(buff, offset, len, out ignored); + return TryReadByteArray(buff, len, out int _); } // NOTE: This method must be retriable WITHOUT replaying a snapshot // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead) + public bool TryReadByteArray(Span buff, int len, out int totalRead) { TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method totalRead = 0; @@ -1421,7 +1420,7 @@ public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead } #endif - Debug.Assert(buff == null || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); + Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); // loop through and read up to array length while (len > 0) @@ -1436,9 +1435,11 @@ public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (buff != null) + if (!buff.IsEmpty) { - Buffer.BlockCopy(_inBuff, _inBytesUsed, buff, offset + totalRead, bytesToRead); + ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); + Span copyTo = buff.Slice(totalRead, bytesToRead); + copyFrom.CopyTo(copyTo); } totalRead += bytesToRead; @@ -1510,7 +1511,7 @@ internal bool TryReadChar(out char value) // If the char isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = '\0'; return false; @@ -1547,7 +1548,7 @@ internal bool TryReadInt16(out short value) // If the int16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = default(short); return false; @@ -1582,7 +1583,7 @@ internal bool TryReadInt32(out int value) // If the int isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 4)) + if (!TryReadByteArray(_bTmp, 4)) { value = 0; return false; @@ -1626,7 +1627,7 @@ internal bool TryReadInt64(out long value) // then use ReadByteArray since the logic is there to take care of that. int bytesRead = 0; - if (!TryReadByteArray(_bTmp, _bTmpRead, 8 - _bTmpRead, out bytesRead)) + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 8 - _bTmpRead, out bytesRead)) { Debug.Assert(_bTmpRead + bytesRead <= 8, "Read more data than required"); _bTmpRead += bytesRead; @@ -1668,7 +1669,7 @@ internal bool TryReadUInt16(out ushort value) // If the uint16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = default(ushort); return false; @@ -1713,7 +1714,7 @@ internal bool TryReadUInt32(out uint value) // then use ReadByteArray since the logic is there to take care of that. int bytesRead = 0; - if (!TryReadByteArray(_bTmp, _bTmpRead, 4 - _bTmpRead, out bytesRead)) + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 4 - _bTmpRead, out bytesRead)) { Debug.Assert(_bTmpRead + bytesRead <= 4, "Read more data than required"); _bTmpRead += bytesRead; @@ -1753,7 +1754,7 @@ internal bool TryReadSingle(out float value) // If the float isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 4)) + if (!TryReadByteArray(_bTmp, 4)) { value = default(float); return false; @@ -1787,7 +1788,7 @@ internal bool TryReadDouble(out double value) // If the double isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 8)) + if (!TryReadByteArray(_bTmp, 8)) { value = default(double); return false; @@ -1827,7 +1828,7 @@ internal bool TryReadString(int length, out string value) _bTmp = new byte[cBytes]; } - if (!TryReadByteArray(_bTmp, 0, cBytes)) + if (!TryReadByteArray(_bTmp, cBytes)) { value = null; return false; @@ -1905,7 +1906,7 @@ internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encodin _bTmp = new byte[length]; } - if (!TryReadByteArray(_bTmp, 0, length)) + if (!TryReadByteArray(_bTmp, length)) { value = null; return false; @@ -2014,7 +2015,7 @@ internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) int value; int bytesToRead = (int)Math.Min(_longlenleft, (ulong)len); - bool result = TryReadByteArray(buff, offset, bytesToRead, out value); + bool result = TryReadByteArray(buff.AsSpan(start: offset), bytesToRead, out value); _longlenleft -= (ulong)bytesToRead; if (!result) { throw SQL.SynchronousCallMayNotPend(); } @@ -2091,7 +2092,7 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offst, int len, out int total buff = newbuf; } - bool result = TryReadByteArray(buff, offst, bytesToRead, out bytesRead); + bool result = TryReadByteArray(buff.AsSpan(start: offst), bytesToRead, out bytesRead); Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); @@ -2138,7 +2139,7 @@ internal bool TrySkipLongBytes(long num) while (num > 0) { cbSkip = (int)Math.Min((long)Int32.MaxValue, num); - if (!TryReadByteArray(null, 0, cbSkip)) + if (!TryReadByteArray(null, cbSkip)) { return false; } @@ -2152,7 +2153,7 @@ internal bool TrySkipLongBytes(long num) internal bool TrySkipBytes(int num) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - return TryReadByteArray(null, 0, num); + return TryReadByteArray(null, num); } ///////////////////////////////////////// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs