Skip to content

Commit 12a26e6

Browse files
author
Davoud Eshtehari
committed
Address comments
1 parent c0dab1c commit 12a26e6

File tree

2 files changed

+208
-88
lines changed
  • src/Microsoft.Data.SqlClient

2 files changed

+208
-88
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs

Lines changed: 104 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ internal DateTime DateTime
205205
}
206206
}
207207

208+
#region Decimal
208209
internal decimal Decimal
209210
{
210211
get
@@ -215,64 +216,44 @@ internal decimal Decimal
215216
{
216217
if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28)
217218
{
219+
// Only removing trailing zeros from a decimal part won't hit its value!
218220
if (_value._numericInfo._scale > 0)
219221
{
220-
SqlDecimal sqlValue = new(_value._numericInfo._precision,
221-
_value._numericInfo._scale,
222-
_value._numericInfo._positive,
223-
_value._numericInfo._data1,
224-
_value._numericInfo._data2,
225-
_value._numericInfo._data3,
226-
_value._numericInfo._data4);
227-
228-
// Remove trail zeros after the point from the end to calculate the valuable floating part
229-
ReadOnlySpan<char> strValue = sqlValue.ToString().AsSpan().TrimEnd('0');
230-
int integral = strValue.IndexOf('.');
231-
int scale = strValue.Length - integral - 1;
232-
int precision = integral + scale + (sqlValue.IsPositive ? 0 : -1);
233-
234-
// Out of range
235-
if ((precision > 29 || scale > 28) && !(precision == 29 && scale == 28))
236-
{
237-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
238-
}
239-
else
222+
int zeroCnt = FindTrailingZerosAndPrec((uint)_value._numericInfo._data1, (uint)_value._numericInfo._data2,
223+
(uint)_value._numericInfo._data3, (uint)_value._numericInfo._data4,
224+
_value._numericInfo._scale, out int precision);
225+
226+
int minScale = _value._numericInfo._scale - zeroCnt; // minimum possible sacle after removing the trailing zeros.
227+
228+
if (zeroCnt > 0 && minScale <= 28 && precision <= 29)
240229
{
241-
// Precision could be 28 or 29
242-
// ex: (precision == 29 && scale == 28)
243-
// valid: (+/-)7.1234567890123456789012345678
244-
// invalid: (+/-)8.1234567890123456789012345678
245-
bool tryMaxPrec = integral == 1 || (integral == 2 && !_value._numericInfo._positive);
246-
int precConvert = 29;
247-
int scaleConvert;
248-
249-
if (!tryMaxPrec && precision != 29)
230+
SqlDecimal sqlValue = new(_value._numericInfo._precision, _value._numericInfo._scale, _value._numericInfo._positive,
231+
_value._numericInfo._data1, _value._numericInfo._data2,
232+
_value._numericInfo._data3, _value._numericInfo._data4);
233+
234+
int integral = precision - minScale;
235+
int newPrec = 29;
236+
237+
if (integral != 1 && precision != 29)
250238
{
251-
precConvert = 28;
239+
newPrec = 28;
252240
}
253-
scaleConvert = precConvert - (precision - scale);
254241

255242
try
256243
{
257-
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert, scaleConvert).Value;
244+
// Precision could be 28 or 29
245+
// ex: (precision == 29 && scale == 28)
246+
// valid: (+/-)7.1234567890123456789012345678
247+
// invalid: (+/-)8.1234567890123456789012345678
248+
return SqlDecimal.ConvertToPrecScale(sqlValue, newPrec, newPrec - integral).Value;
258249
}
259250
catch (OverflowException)
260251
{
261-
if (tryMaxPrec && scale < 28)
262-
{
263-
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert - 1, scaleConvert).Value;
264-
}
265-
else
266-
{
267-
throw;
268-
}
252+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
269253
}
270254
}
271255
}
272-
else
273-
{
274-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
275-
}
256+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
276257
}
277258
return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale);
278259
}
@@ -291,6 +272,85 @@ internal decimal Decimal
291272
}
292273
}
293274

275+
/// <summary>
276+
/// Returns number of trailing zeros using the supplied parameters.
277+
/// </summary>
278+
/// <param name="data1">An 32-bit unsigned integer which will be combined with data2, data3, and data4</param>
279+
/// <param name="data2">An 32-bit unsigned integer which will be combined with data1, data3, and data4</param>
280+
/// <param name="data3">An 32-bit unsigned integer which will be combined with data1, data2, and data4</param>
281+
/// <param name="data4">An 32-bit unsigned integer which will be combined with data1, data2, and data3</param>
282+
/// <param name="scale">The number of decimal places</param>
283+
/// <param name="valuablePrecision">OUT |The number of digits without trailing zeros</param>
284+
/// <returns>Number of trailing zeros</returns>
285+
private static int FindTrailingZerosAndPrec(uint data1, uint data2, uint data3, uint data4, byte scale, out int valuablePrecision)
286+
{
287+
// Make local copy of data to avoid modifying input.
288+
uint[] rgulNumeric = new uint[4] { data1, data2, data3, data4 };
289+
int zeroCnt = 0; //Number of trailing zero digits
290+
int precCnt = 0; //Valuable precision
291+
uint uiRem = 0; //Remainder of a division by 10
292+
int len = 4; // Max possible items
293+
294+
//Retrieve each digit from the lowest significant digit
295+
while (len > 1 || rgulNumeric[0] != 0)
296+
{
297+
SqlDecimalDivBy(rgulNumeric, ref len, 10, out uiRem);
298+
if (uiRem == 0 && precCnt == 0)
299+
{
300+
zeroCnt++;
301+
}
302+
else
303+
{
304+
precCnt++;
305+
}
306+
}
307+
308+
if (uiRem == 0)
309+
{
310+
zeroCnt = scale;
311+
}
312+
313+
// if scale of the number has not been reached, pad remaining number with zeros.
314+
if (zeroCnt + precCnt <= scale)
315+
{
316+
precCnt = scale - zeroCnt + 1;
317+
}
318+
valuablePrecision = precCnt;
319+
return zeroCnt;
320+
}
321+
322+
/// <summary>
323+
/// Multi-precision one super-digit divide in place.
324+
/// U = U / D,
325+
/// R = U % D
326+
/// (Length of U can decrease)
327+
/// </summary>
328+
/// <param name="data">InOut | U</param>
329+
/// <param name="len">InOut | Number of items with non-zero value in U between 1 to 4</param>
330+
/// <param name="divisor">In | D</param>
331+
/// <param name="remainder">Out | R</param>
332+
private static void SqlDecimalDivBy(uint[] data, ref int len, uint divisor, out uint remainder)
333+
{
334+
uint uiCarry = 0;
335+
ulong ulAccum;
336+
ulong ulDivisor = (ulong)divisor;
337+
int iLen = len;
338+
339+
while (iLen > 0)
340+
{
341+
iLen--;
342+
ulAccum = (((ulong)uiCarry) << 32) + (ulong)(data[iLen]);
343+
data[iLen] = (uint)(ulAccum / ulDivisor);
344+
uiCarry = (uint)(ulAccum - (ulong)data[iLen] * ulDivisor); // (ULONG) (ulAccum % divisor)
345+
}
346+
remainder = uiCarry;
347+
348+
// Normalize multi-precision number - remove leading zeroes
349+
while (len > 1 && data[len - 1] == 0)
350+
{ len--; }
351+
}
352+
#endregion
353+
294354
internal double Double
295355
{
296356
get

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs

Lines changed: 104 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ internal DateTime DateTime
202202
}
203203
}
204204

205+
#region Decimal
205206
internal decimal Decimal
206207
{
207208
get
@@ -212,64 +213,44 @@ internal decimal Decimal
212213
{
213214
if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28)
214215
{
216+
// Only removing trailing zeros from a decimal part won't hit its value!
215217
if (_value._numericInfo._scale > 0)
216218
{
217-
SqlDecimal sqlValue = new(_value._numericInfo._precision,
218-
_value._numericInfo._scale,
219-
_value._numericInfo._positive,
220-
_value._numericInfo._data1,
221-
_value._numericInfo._data2,
222-
_value._numericInfo._data3,
223-
_value._numericInfo._data4);
224-
225-
// Remove trail zeros after the point from the end to calculate the valuable floating part
226-
ReadOnlySpan<char> strValue = sqlValue.ToString().AsSpan().TrimEnd('0');
227-
int integral = strValue.IndexOf('.');
228-
int scale = strValue.Length - integral - 1;
229-
int precision = integral + scale + (sqlValue.IsPositive ? 0 : -1);
230-
231-
// Out of range
232-
if ((precision > 29 || scale > 28) && !(precision == 29 && scale == 28))
233-
{
234-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
235-
}
236-
else
219+
int zeroCnt = FindTrailingZerosAndPrec((uint)_value._numericInfo._data1, (uint)_value._numericInfo._data2,
220+
(uint)_value._numericInfo._data3, (uint)_value._numericInfo._data4,
221+
_value._numericInfo._scale, out int precision);
222+
223+
int minScale = _value._numericInfo._scale - zeroCnt; // minimum possible sacle after removing the trailing zeros.
224+
225+
if (zeroCnt > 0 && minScale <= 28 && precision <= 29)
237226
{
238-
// Precision could be 28 or 29
239-
// ex: (precision == 29 && scale == 28)
240-
// valid: (+/-)7.1234567890123456789012345678
241-
// invalid: (+/-)8.1234567890123456789012345678
242-
bool tryMaxPrec = integral == 1 || (integral == 2 && !_value._numericInfo._positive);
243-
int precConvert = 29;
244-
int scaleConvert;
245-
246-
if (!tryMaxPrec && precision != 29)
227+
SqlDecimal sqlValue = new(_value._numericInfo._precision, _value._numericInfo._scale, _value._numericInfo._positive,
228+
_value._numericInfo._data1, _value._numericInfo._data2,
229+
_value._numericInfo._data3, _value._numericInfo._data4);
230+
231+
int integral = precision - minScale;
232+
int newPrec = 29;
233+
234+
if (integral != 1 && precision != 29)
247235
{
248-
precConvert = 28;
236+
newPrec = 28;
249237
}
250-
scaleConvert = precConvert - (precision - scale);
251238

252239
try
253240
{
254-
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert, scaleConvert).Value;
241+
// Precision could be 28 or 29
242+
// ex: (precision == 29 && scale == 28)
243+
// valid: (+/-)7.1234567890123456789012345678
244+
// invalid: (+/-)8.1234567890123456789012345678
245+
return SqlDecimal.ConvertToPrecScale(sqlValue, newPrec, newPrec - integral).Value;
255246
}
256247
catch (OverflowException)
257248
{
258-
if (tryMaxPrec && scale < 28)
259-
{
260-
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert - 1, scaleConvert).Value;
261-
}
262-
else
263-
{
264-
throw;
265-
}
249+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
266250
}
267251
}
268252
}
269-
else
270-
{
271-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
272-
}
253+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
273254
}
274255
return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale);
275256
}
@@ -288,6 +269,85 @@ internal decimal Decimal
288269
}
289270
}
290271

272+
/// <summary>
273+
/// Returns number of trailing zeros using the supplied parameters.
274+
/// </summary>
275+
/// <param name="data1">An 32-bit unsigned integer which will be combined with data2, data3, and data4</param>
276+
/// <param name="data2">An 32-bit unsigned integer which will be combined with data1, data3, and data4</param>
277+
/// <param name="data3">An 32-bit unsigned integer which will be combined with data1, data2, and data4</param>
278+
/// <param name="data4">An 32-bit unsigned integer which will be combined with data1, data2, and data3</param>
279+
/// <param name="scale">The number of decimal places</param>
280+
/// <param name="valuablePrecision">OUT |The number of digits without trailing zeros</param>
281+
/// <returns>Number of trailing zeros</returns>
282+
private static int FindTrailingZerosAndPrec(uint data1, uint data2, uint data3, uint data4, byte scale, out int valuablePrecision)
283+
{
284+
// Make local copy of data to avoid modifying input.
285+
uint[] rgulNumeric = new uint[4] { data1, data2, data3, data4 };
286+
int zeroCnt = 0; //Number of trailing zero digits
287+
int precCnt = 0; //Valuable precision
288+
uint uiRem = 0; //Remainder of a division by 10
289+
int len = 4; // Max possible items
290+
291+
//Retrieve each digit from the lowest significant digit
292+
while (len > 1 || rgulNumeric[0] != 0)
293+
{
294+
SqlDecimalDivBy(rgulNumeric, ref len, 10, out uiRem);
295+
if (uiRem == 0 && precCnt == 0)
296+
{
297+
zeroCnt++;
298+
}
299+
else
300+
{
301+
precCnt++;
302+
}
303+
}
304+
305+
if (uiRem == 0)
306+
{
307+
zeroCnt = scale;
308+
}
309+
310+
// if scale of the number has not been reached, pad remaining number with zeros.
311+
if (zeroCnt + precCnt <= scale)
312+
{
313+
precCnt = scale - zeroCnt + 1;
314+
}
315+
valuablePrecision = precCnt;
316+
return zeroCnt;
317+
}
318+
319+
/// <summary>
320+
/// Multi-precision one super-digit divide in place.
321+
/// U = U / D,
322+
/// R = U % D
323+
/// (Length of U can decrease)
324+
/// </summary>
325+
/// <param name="data">InOut | U</param>
326+
/// <param name="len">InOut | Number of items with non-zero value in U between 1 to 4</param>
327+
/// <param name="divisor">In | D</param>
328+
/// <param name="remainder">Out | R</param>
329+
private static void SqlDecimalDivBy(uint[] data, ref int len, uint divisor, out uint remainder)
330+
{
331+
uint uiCarry = 0;
332+
ulong ulAccum;
333+
ulong ulDivisor = (ulong)divisor;
334+
int iLen = len;
335+
336+
while (iLen > 0)
337+
{
338+
iLen--;
339+
ulAccum = (((ulong)uiCarry) << 32) + (ulong)(data[iLen]);
340+
data[iLen] = (uint)(ulAccum / ulDivisor);
341+
uiCarry = (uint)(ulAccum - (ulong)data[iLen] * ulDivisor); // (ULONG) (ulAccum % divisor)
342+
}
343+
remainder = uiCarry;
344+
345+
// Normalize multi-precision number - remove leading zeroes
346+
while (len > 1 && data[len - 1] == 0)
347+
{ len--; }
348+
}
349+
#endregion
350+
291351
internal double Double
292352
{
293353
get

0 commit comments

Comments
 (0)