diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs index dc1abf1c9d..385a49c802 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs @@ -1352,7 +1352,7 @@ internal void SetToDateTime2(DateTime dateTime, byte scale) } #endif - internal void SetToDecimal(byte precision, byte scale, bool positive, int[] bits) + internal void SetToDecimal(byte precision, byte scale, bool positive, ReadOnlySpan bits) { Debug.Assert(IsEmpty, "setting value a second time?"); _value._numericInfo._precision = precision; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs index fb9cdbb8e0..51eacad552 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -2250,7 +2250,13 @@ private byte ValueScaleCore(object value) { if (value is decimal decimalValue) { - return (byte)((decimal.GetBits(decimalValue)[3] & 0x00ff0000) >> 0x10); +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits(decimalValue, decimalBits); +#else + int[] decimalBits = decimal.GetBits(decimalValue); +#endif + return (byte)((decimalBits[3] & 0x00ff0000) >> 0x10); } return 0; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs index eb72b8d6b1..286a3b609d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6782,7 +6782,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt // Now read the 4 next integers which contain the actual value. length = checked((int)length - 1); - int[] bits = new int[4]; + Span bits = stackalloc int[4]; int decLength = length >> 2; ReadOnlySpan span = unencryptedBytes.AsSpan(); for (int i = 0; i < decLength; i++) @@ -7702,8 +7702,15 @@ internal Task WriteSqlVariantValue(object value, int length, int offset, TdsPars case TdsEnums.SQLNUMERICN: { +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits((decimal)value, decimalBits); +#else + int[] decimalBits = decimal.GetBits((decimal)value); +#endif + stateObj.WriteByte(mt.Precision); //propbytes: precision - stateObj.WriteByte((byte)((decimal.GetBits((decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale + stateObj.WriteByte((byte)((decimalBits[3] & 0x00ff0000) >> 0x10)); // propbytes: scale WriteDecimal((decimal)value, stateObj); break; } @@ -7864,9 +7871,15 @@ internal Task WriteSqlVariantDataRowValue(object value, TdsParserStateObject sta case TdsEnums.SQLNUMERICN: { +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits((decimal)value, decimalBits); +#else + int[] decimalBits = decimal.GetBits((decimal)value); +#endif WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj); stateObj.WriteByte(metatype.Precision); //propbytes: precision - stateObj.WriteByte((byte)((decimal.GetBits((decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale + stateObj.WriteByte((byte)((decimalBits[3] & 0x00ff0000) >> 0x10)); // propbytes: scale WriteDecimal((decimal)value, stateObj); break; } @@ -7923,7 +7936,12 @@ private byte[] SerializeSqlMoney(SqlMoney value, int length, TdsParserStateObjec private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) { // UNDONE: can I use SqlMoney.ToInt64()? +#if NET + Span bits = stackalloc int[4]; + decimal.GetBits(value.Value, bits); +#else int[] bits = decimal.GetBits(value.Value); +#endif // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by) bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000))); @@ -7956,7 +7974,12 @@ private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stat private byte[] SerializeCurrency(Decimal value, int length, TdsParserStateObject stateObj) { SqlMoney m = new SqlMoney(value); - int[] bits = Decimal.GetBits(m.Value); +#if NET + Span bits = stackalloc int[4]; + decimal.GetBits(m.Value, bits); +#else + int[] bits = decimal.GetBits(m.Value); +#endif // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by) bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000))); @@ -8001,7 +8024,12 @@ private byte[] SerializeCurrency(Decimal value, int length, TdsParserStateObject private void WriteCurrency(decimal value, int length, TdsParserStateObject stateObj) { SqlMoney m = new SqlMoney(value); +#if NET + Span bits = stackalloc int[4]; + decimal.GetBits(m.Value, bits); +#else int[] bits = decimal.GetBits(m.Value); +#endif // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by) bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000))); @@ -8138,8 +8166,8 @@ private TdsOperationStatus TryReadSqlDecimal(SqlBuffer value, int length, byte p length = checked((int)length - 1); - int[] bits; - result = TryReadDecimalBits(length, stateObj, out bits); + Span bits = stackalloc int[4]; + result = TryReadDecimalBits(length, stateObj, bits); if (result != TdsOperationStatus.Done) { return result; @@ -8151,31 +8179,16 @@ private TdsOperationStatus TryReadSqlDecimal(SqlBuffer value, int length, byte p // @devnote: length should be size of decimal without the sign // @devnote: sign should have already been read off the wire - private TdsOperationStatus TryReadDecimalBits(int length, TdsParserStateObject stateObj, out int[] bits) + private TdsOperationStatus TryReadDecimalBits(int length, TdsParserStateObject stateObj, Span bits) { - bits = stateObj._decimalBits; // used alloc'd array if we have one already - int i; - - if (bits == null) - { - bits = new int[4]; - stateObj._decimalBits = bits; - } - else - { - for (i = 0; i < bits.Length; i++) - { - bits[i] = 0; - } - } - + Debug.Assert(bits.Length == 4); Debug.Assert((length > 0) && (length <= TdsEnums.MAX_NUMERIC_LEN - 1) && (length % 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data"); int decLength = length >> 2; - for (i = 0; i < decLength; i++) + for (int i = 0; i < decLength; i++) { // up to 16 bytes of data following the sign byte TdsOperationStatus result = stateObj.TryReadInt32(out bits[i]); @@ -8201,7 +8214,13 @@ internal static SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale) internal static decimal AdjustDecimalScale(decimal value, int newScale) { - int oldScale = (decimal.GetBits(value)[3] & 0x00ff0000) >> 0x10; +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits(value, decimalBits); +#else + int[] decimalBits = decimal.GetBits(value); +#endif + int oldScale = (decimalBits[3] & 0x00ff0000) >> 0x10; if (newScale != oldScale) { @@ -8283,7 +8302,12 @@ internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) private byte[] SerializeDecimal(decimal value, TdsParserStateObject stateObj) { - int[] decimalBits = Decimal.GetBits(value); +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits(value, decimalBits); +#else + int[] decimalBits = decimal.GetBits(value); +#endif if (stateObj._bDecimalBytes == null) { stateObj._bDecimalBytes = new byte[17]; @@ -8338,8 +8362,12 @@ struct { private void WriteDecimal(decimal value, TdsParserStateObject stateObj) { - stateObj._decimalBits = decimal.GetBits(value); - Debug.Assert(stateObj._decimalBits != null, "decimalBits should be filled in at TdsExecuteRPC time"); +#if NET + Span decimalBits = stackalloc int[4]; + decimal.GetBits(value, decimalBits); +#else + int[] decimalBits = decimal.GetBits(value); +#endif /* Returns a binary representation of a Decimal. The return value is an integer @@ -8361,7 +8389,7 @@ struct { */ // write the sign (note that COM and SQL are opposite) - if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000)) + if ((decimalBits[3] & 0x80000000) == 0x80000000) { stateObj.WriteByte(0); } @@ -8370,9 +8398,9 @@ struct { stateObj.WriteByte(1); } - WriteInt(stateObj._decimalBits[0], stateObj); - WriteInt(stateObj._decimalBits[1], stateObj); - WriteInt(stateObj._decimalBits[2], stateObj); + WriteInt(decimalBits[0], stateObj); + WriteInt(decimalBits[1], stateObj); + WriteInt(decimalBits[2], stateObj); WriteInt(0, stateObj); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 6a9b2d7369..69ae4ccd9d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -223,7 +223,6 @@ private enum SnapshotStatus // TDS stream processing variables internal ulong _longlen; // plp data length indicator internal ulong _longlenleft; // Length of data left to read (64 bit lengths) - internal int[] _decimalBits; // scratch buffer for decimal/numeric data internal byte[] _bTmp = new byte[TdsEnums.SQL2005_HEADER_LEN]; // Scratch buffer for misc use internal int _bTmpRead; // Counter for number of temporary bytes read internal Decoder _plpdecoder; // Decoder object to process plp character data