diff options
author | Jan Kotas <jkotas@microsoft.com> | 2017-10-03 20:43:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-03 20:43:59 +0300 |
commit | e3f80eaec6f16742b77925f822d8e513ba8876e5 (patch) | |
tree | 8583005fb15f003f2733975f6566bcd1c1ff8ef0 | |
parent | b889e1b1eb5674c8bdac6da1421256099cdb495d (diff) | |
parent | 5a0aef044fa14602210ea06533796186c3790c11 (diff) |
Merge pull request #4652 from dotnet/master
Merge master to nmirror
15 files changed, 976 insertions, 1072 deletions
diff --git a/dependencies.props b/dependencies.props index a536e2a9a..ec96f60ea 100644 --- a/dependencies.props +++ b/dependencies.props @@ -4,7 +4,7 @@ <ObjectWriterVersion>1.0.19-prerelease-00001</ObjectWriterVersion> <CoreFxVersion>4.5.0-preview1-25729-01</CoreFxVersion> <CoreFxUapVersion>4.6.0-preview1-25729-01</CoreFxUapVersion> - <MicrosoftNETCoreNativeVersion>2.0.0-beta-25021-03</MicrosoftNETCoreNativeVersion> + <MicrosoftNETCoreNativeVersion>2.1.0-preview1-25730-01</MicrosoftNETCoreNativeVersion> <MicrosoftNETCoreAppPackageVersion>2.0.0-preview2-25312-01</MicrosoftNETCoreAppPackageVersion> <XunitNetcoreExtensionsVersion>1.0.1-prerelease-01616-05</XunitNetcoreExtensionsVersion> </PropertyGroup> diff --git a/src/Framework/Framework-native.depproj b/src/Framework/Framework-native.depproj index cc3093531..8719e1c85 100644 --- a/src/Framework/Framework-native.depproj +++ b/src/Framework/Framework-native.depproj @@ -9,6 +9,8 @@ <NuGetTargetMoniker>.NETCoreApp,Version=v2.0</NuGetTargetMoniker> <TargetFramework>netcoreapp2.0</TargetFramework> <RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers> + <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('osx.'))">osx-x64</RuntimeIdentifiers> + <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">linux-x64</RuntimeIdentifiers> <RidSpecificAssets>true</RidSpecificAssets> </PropertyGroup> diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs new file mode 100644 index 000000000..c69088414 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_LoadICU")] + internal static extern int LoadICU(); + } +} diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 0831e58af..6f24b8782 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -644,6 +644,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Calendar.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Casing.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Collation.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.ICU.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Idna.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Locale.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Normalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" /> diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index cd81f6ce9..403aa3bdc 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -205,6 +205,8 @@ <Compile Include="System\Empty.cs" /> <Compile Include="System\Globalization\CharUnicodeInfoData.cs" /> <Compile Include="System\Globalization\GlobalizationMode.cs" /> + <Compile Include="System\Globalization\GlobalizationMode.Windows.cs" Condition="'$(TargetsWindows)'=='true'" /> + <Compile Include="System\Globalization\GlobalizationMode.Unix.cs" Condition="'$(TargetsUnix)'=='true'" /> <Compile Include="System\Globalization\RegionInfo.cs" /> <Compile Include="System\Guid.CoreRT.cs" /> <Compile Include="System\InvokeUtils.cs" /> diff --git a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs index 5e67f3f61..af85d65df 100644 --- a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs +++ b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs @@ -2,8 +2,11 @@ // 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.Diagnostics; +using System; +using System.Globalization; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; namespace System { @@ -36,19 +39,14 @@ namespace System internal int Scale { - get { return (byte)(uflags >> ScaleShift); } + get { return (int)((uflags & ScaleMask) >> ScaleShift); } set { uflags = (uflags & ~ScaleMask) | ((uint)value << ScaleShift); } } private ulong Low64 { -#if BIGENDIAN get { return ((ulong)umid << 32) | ulo; } set { umid = (uint)(value >> 32); ulo = (uint)value; } -#else - get => ulomidLE; - set => ulomidLE = value; -#endif } #region APIs need by number formatting. @@ -73,7 +71,7 @@ namespace System /// <summary> /// Class that contains all the mathematical calculations for decimal. Most of which have been ported from oleaut32. /// </summary> - private static class DecCalc + private class DecCalc { // Constant representing the negative number that is the closest possible // Decimal value to -0m. @@ -85,19 +83,24 @@ namespace System private const int DEC_SCALE_MAX = 28; + private const uint OVFL_MAX_9_HI = 4; + private const uint OVFL_MAX_9_MID = 1266874889; + private const uint OVFL_MAX_9_LO = 3047500985; + private const uint OVFL_MAX_5_HI = 42949; + private const uint OVFL_MAX_1_HI = 429496729; + private const uint SNGBIAS = 126; private const uint DBLBIAS = 1022; private const uint TenToPowerNine = 1000000000; + private const uint TenToPowerTenDiv4 = 2500000000; private static readonly Split64 s_tenToPowerEighteen = new Split64() { int64 = 1000000000000000000 }; // The maximum power of 10 that a 32 bit integer can store private const Int32 MaxInt32Scale = 9; - // The maximum power of 10 that a 64 bit integer can store - private const Int32 MaxInt64Scale = 19; // Fast access for 10^n where n is 0-9 - private static readonly UInt32[] s_powers10 = new UInt32[] { + private static UInt32[] s_powers10 = new UInt32[] { 1, 10, 100, @@ -110,30 +113,7 @@ namespace System 1000000000 }; - // Fast access for 10^n where n is 1-19 - private static readonly ulong[] s_ulongPowers10 = new ulong[] { - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000, - }; - - private static readonly double[] s_doublePowers10 = new double[] { + private static double[] s_doublePowers10 = new double[] { 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, @@ -201,6 +181,30 @@ namespace System return sdl.int64; } + private static uint FullDiv64By32(ref ulong pdlNum, uint ulDen) + { + Split64 sdlTmp = new Split64(); + Split64 sdlRes = new Split64(); + + sdlTmp.int64 = pdlNum; + sdlRes.High32 = 0; + + if (sdlTmp.High32 >= ulDen) + { + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlRes.Low32 = sdlTmp.High32; + sdlRes.int64 = DivMod64by32(sdlRes.int64, ulDen); + sdlTmp.High32 = sdlRes.High32; + sdlRes.High32 = sdlRes.Low32; + } + + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); + sdlRes.Low32 = sdlTmp.Low32; + pdlNum = sdlRes.int64; + return sdlTmp.High32; + } + private static ulong UInt32x32To64(uint a, uint b) { return (ulong)a * (ulong)b; @@ -232,7 +236,7 @@ namespace System * Div96By32 * * Entry: - * bufNum - 96-bit dividend as array of ULONGs, least-sig first + * rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first * ulDen - 32-bit divisor. * * Purpose: @@ -246,80 +250,43 @@ namespace System * None. * ***********************************************************************/ - private static uint Div96By32(ref Buf12 bufNum, uint ulDen) + private static uint Div96By32(uint[] rgulNum, uint ulDen) { - // TODO: https://github.com/dotnet/coreclr/issues/3439 - ulong tmp, div; - if (bufNum.U2 != 0) - { - tmp = bufNum.High64; - div = tmp / ulDen; - bufNum.High64 = div; - tmp = ((tmp - div * ulDen) << 32) | bufNum.U0; - uint div32 = (uint)(tmp / ulDen); - bufNum.U0 = div32; - return (uint)tmp - div32 * ulDen; - } + Split64 sdlTmp = new Split64(); - tmp = bufNum.Low64; - div = tmp / ulDen; - bufNum.Low64 = div; - return (uint)(tmp - div * ulDen); - } + sdlTmp.High32 = 0; - private static uint Div96ByConst100000000(ref Buf12 bufNum) - { - const uint ulDen = 100000000; - ulong tmp = bufNum.High64; - ulong div = tmp / ulDen; - bufNum.High64 = div; - tmp = ((tmp - div * ulDen) << 32) | bufNum.U0; - uint div32 = (uint)(tmp / ulDen); - bufNum.U0 = div32; - return (uint)tmp - div32 * ulDen; - } + if (rgulNum[2] != 0) + goto Div3Word; - private static uint Div96ByConst10000(ref Buf12 bufNum) - { - const uint ulDen = 10000; - ulong tmp = bufNum.High64; - ulong div = tmp / ulDen; - bufNum.High64 = div; - tmp = ((tmp - div * ulDen) << 32) | bufNum.U0; - uint div32 = (uint)(tmp / ulDen); - bufNum.U0 = div32; - return (uint)tmp - div32 * ulDen; - } + if (rgulNum[1] >= ulDen) + goto Div2Word; - private static uint Div96ByConst100(ref Buf12 bufNum) - { - const uint ulDen = 100; - ulong tmp = bufNum.High64; - ulong div = tmp / ulDen; - bufNum.High64 = div; - tmp = ((tmp - div * ulDen) << 32) | bufNum.U0; - uint div32 = (uint)(tmp / ulDen); - bufNum.U0 = div32; - return (uint)tmp - div32 * ulDen; - } + sdlTmp.High32 = rgulNum[1]; + rgulNum[1] = 0; + goto Div1Word; - private static uint Div96ByConst10(ref Buf12 bufNum) - { - const uint ulDen = 10; - ulong tmp = bufNum.High64; - ulong div = tmp / ulDen; - bufNum.High64 = div; - tmp = ((tmp - div * ulDen) << 32) | bufNum.U0; - uint div32 = (uint)(tmp / ulDen); - bufNum.U0 = div32; - return (uint)tmp - div32 * ulDen; + Div3Word: + sdlTmp.Low32 = rgulNum[2]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); + rgulNum[2] = sdlTmp.Low32; + Div2Word: + sdlTmp.Low32 = rgulNum[1]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); + rgulNum[1] = sdlTmp.Low32; + Div1Word: + sdlTmp.Low32 = rgulNum[0]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); + rgulNum[0] = sdlTmp.Low32; + + return sdlTmp.High32; } /*** * Div96By64 * * Entry: - * bufNum - 96-bit dividend as array of ULONGs, least-sig first + * rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first * sdlDen - 64-bit divisor. * * Purpose: @@ -334,87 +301,77 @@ namespace System * None. * ***********************************************************************/ - private static uint Div96By64(ref Buf12 bufNum, ulong den) + private static uint Div96By64(System.Collections.Generic.IList<uint> rgulNum, Split64 sdlDen) { - uint quo; - ulong num; - uint num2 = bufNum.U2; - if (num2 == 0) - { - num = bufNum.Low64; - if (num < den) - // Result is zero. Entire dividend is remainder. - return 0; + Split64 sdlQuo = new Split64(); + Split64 sdlNum = new Split64(); + Split64 sdlProd = new Split64(); - // TODO: https://github.com/dotnet/coreclr/issues/3439 - quo = (uint)(num / den); - num -= quo * den; // remainder - bufNum.Low64 = num; - return quo; - } + sdlNum.Low32 = rgulNum[0]; - uint denHigh32 = (uint)(den >> 32); - if (num2 >= denHigh32) + if (rgulNum[2] >= sdlDen.High32) { // Divide would overflow. Assume a quotient of 2^32, and set // up remainder accordingly. // - num = bufNum.Low64; - num -= den << 32; - quo = 0; + sdlNum.High32 = rgulNum[1] - sdlDen.Low32; + sdlQuo.Low32 = 0; // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // do { - quo--; - num += den; - } while (num >= den); + sdlQuo.Low32--; + sdlNum.int64 += sdlDen.int64; + } while (sdlNum.int64 >= sdlDen.int64); - bufNum.Low64 = num; - return quo; + goto Done; } // Hardware divide won't overflow // - ulong num64 = bufNum.High64; - if (num64 < denHigh32) + if (rgulNum[2] == 0 && rgulNum[1] < sdlDen.High32) // Result is zero. Entire dividend is remainder. // return 0; - // TODO: https://github.com/dotnet/coreclr/issues/3439 - quo = (uint)(num64 / denHigh32); - num = bufNum.U0 | ((num64 - quo * denHigh32) << 32); // remainder + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlQuo.Low32 = rgulNum[1]; + sdlQuo.High32 = rgulNum[2]; + sdlQuo.int64 = DivMod64by32(sdlQuo.int64, sdlDen.High32); + sdlNum.High32 = sdlQuo.High32; // remainder // Compute full remainder, rem = dividend - (quo * divisor). // - ulong prod = UInt32x32To64(quo, (uint)den); // quo * lo divisor - num -= prod; + sdlProd.int64 = UInt32x32To64(sdlQuo.Low32, sdlDen.Low32); // quo * lo divisor + sdlNum.int64 -= sdlProd.int64; - if (num > ~prod) + if (sdlNum.int64 > ~sdlProd.int64) { // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // do { - quo--; - num += den; - } while (num >= den); + sdlQuo.Low32--; + sdlNum.int64 += sdlDen.int64; + } while (sdlNum.int64 >= sdlDen.int64); } - bufNum.Low64 = num; - return quo; + Done: + rgulNum[0] = sdlNum.Low32; + rgulNum[1] = sdlNum.High32; + return sdlQuo.Low32; } /*** * Div128By96 * * Entry: - * bufNum - 128-bit dividend as array of ULONGs, least-sig first - * bufDen - 96-bit divisor. + * rgulNum - Pointer to 128-bit dividend as array of ULONGs, least-sig first + * rgulDen - Pointer to 96-bit divisor. * * Purpose: * Do partial divide, yielding 32-bit result and 96-bit remainder. @@ -432,77 +389,83 @@ namespace System * None. * ***********************************************************************/ - private static uint Div128By96(ref Buf16 bufNum, ref Buf12 bufDen) + private static uint Div128By96(uint[] rgulNum, uint[] rgulDen) { - ulong dividend = bufNum.High64; - uint den = bufDen.U2; - if (dividend < den) + Split64 sdlQuo = new Split64(); + Split64 sdlNum = new Split64(); + Split64 sdlProd1 = new Split64(); + Split64 sdlProd2 = new Split64(); + + sdlNum.Low32 = rgulNum[0]; + sdlNum.High32 = rgulNum[1]; + + if (rgulNum[3] == 0 && rgulNum[2] < rgulDen[2]) // Result is zero. Entire dividend is remainder. // return 0; - // TODO: https://github.com/dotnet/coreclr/issues/3439 - uint quo = (uint)(dividend / den); - uint remainder = (uint)dividend - quo * den; + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlQuo.Low32 = rgulNum[2]; + sdlQuo.High32 = rgulNum[3]; + sdlQuo.int64 = DivMod64by32(sdlQuo.int64, rgulDen[2]); // Compute full remainder, rem = dividend - (quo * divisor). // - ulong prod1 = UInt32x32To64(quo, bufDen.U0); // quo * lo divisor - ulong prod2 = UInt32x32To64(quo, bufDen.U1); // quo * mid divisor - prod2 += prod1 >> 32; - prod1 = (uint)prod1 | (prod2 << 32); - prod2 >>= 32; + sdlProd1.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[0]); // quo * lo divisor + sdlProd2.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[1]); // quo * mid divisor + sdlProd2.int64 += sdlProd1.High32; + sdlProd1.High32 = sdlProd2.Low32; - ulong num = bufNum.Low64; - num -= prod1; - remainder -= (uint)prod2; + sdlNum.int64 -= sdlProd1.int64; + rgulNum[2] = sdlQuo.High32 - sdlProd2.High32; // sdlQuo.Hi is remainder // Propagate carries // - if (num > ~prod1) + bool fallthru = false; + if (sdlNum.int64 > ~sdlProd1.int64) { - remainder--; - if (remainder < ~(uint)prod2) - goto PosRem; + rgulNum[2]--; + if (rgulNum[2] >= ~sdlProd2.High32) + fallthru = true; } - else if (remainder <= ~(uint)prod2) - goto PosRem; + if (fallthru || rgulNum[2] > ~sdlProd2.High32) { // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // - prod1 = bufDen.Low64; + sdlProd1.Low32 = rgulDen[0]; + sdlProd1.High32 = rgulDen[1]; for (;;) { - quo--; - num += prod1; - remainder += den; + sdlQuo.Low32--; + sdlNum.int64 += sdlProd1.int64; + rgulNum[2] += rgulDen[2]; - if (num < prod1) + if (sdlNum.int64 < sdlProd1.int64) { // Detected carry. Check for carry out of top // before adding it in. // - if (remainder++ < den) + if (rgulNum[2]++ < rgulDen[2]) break; } - if (remainder < den) + if (rgulNum[2] < rgulDen[2]) break; // detected carry } } -PosRem: - bufNum.Low64 = num; - bufNum.U2 = remainder; - return quo; + rgulNum[0] = sdlNum.Low32; + rgulNum[1] = sdlNum.High32; + return sdlQuo.Low32; } /*** * IncreaseScale * * Entry: - * bufNum - 96-bit number as array of ULONGs, least-sig first + * rgulNum - Pointer to 96-bit number as array of ULONGs, least-sig first * ulPwr - Scale factor to multiply by * * Purpose: @@ -516,34 +479,25 @@ PosRem: * None. * ***********************************************************************/ - private static uint IncreaseScale(ref Buf12 bufNum, uint ulPwr) + private static uint IncreaseScale(uint[] rgulNum, uint ulPwr) { - ulong tmp = UInt32x32To64(bufNum.U0, ulPwr); - bufNum.U0 = (uint)tmp; - tmp >>= 32; - tmp += UInt32x32To64(bufNum.U1, ulPwr); - bufNum.U1 = (uint)tmp; - tmp >>= 32; - tmp += UInt32x32To64(bufNum.U2, ulPwr); - bufNum.U2 = (uint)tmp; - return (uint)(tmp >> 32); - } + Split64 sdlTmp = new Split64(); - private static void IncreaseScale64(ref Buf12 bufNum, uint ulPwr) - { - ulong tmp = UInt32x32To64(bufNum.U0, ulPwr); - bufNum.U0 = (uint)tmp; - tmp >>= 32; - tmp += UInt32x32To64(bufNum.U1, ulPwr); - bufNum.High64 = tmp; + sdlTmp.int64 = UInt32x32To64(rgulNum[0], ulPwr); + rgulNum[0] = sdlTmp.Low32; + sdlTmp.int64 = UInt32x32To64(rgulNum[1], ulPwr) + sdlTmp.High32; + rgulNum[1] = sdlTmp.Low32; + sdlTmp.int64 = UInt32x32To64(rgulNum[2], ulPwr) + sdlTmp.High32; + rgulNum[2] = sdlTmp.Low32; + return sdlTmp.High32; } /*** * ScaleResult * * Entry: - * bufRes - Array of ULONGs with value, least-significant first. - * iHiRes - Index of last non-zero value in bufRes. + * rgulRes - Array of ULONGs with value, least-significant first. + * iHiRes - Index of last non-zero value in rgulRes. * iScale - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX * * Purpose: @@ -551,14 +505,18 @@ PosRem: * Perform needed scaling. Adjust scale factor accordingly. * * Exit: - * bufRes updated in place, always 3 ULONGs. - * New scale factor returned. + * rgulRes updated in place, always 3 ULONGs. + * New scale factor returned, -1 if overflow error. * ***********************************************************************/ - private static unsafe int ScaleResult(Buf24* bufRes, uint iHiRes, int iScale) + private static int ScaleResult(uint[] rgulRes, int iHiRes, int iScale) { - Debug.Assert(iHiRes < bufRes->Length); - uint* rgulRes = (uint*)bufRes; + int iNewScale; + int iCur; + uint ulPwr; + uint ulTmp; + uint ulSticky; + Split64 sdlTmp = new Split64(); // See if we need to scale the result. The combined scale must // be <= DEC_SCALE_MAX and the upper 96 bits must be zero. @@ -567,35 +525,37 @@ PosRem: // the upper 96 bits zero. iHiRes is the index into rgulRes[] // of the highest non-zero ULONG. // - int iNewScale = 0; - if (iHiRes > 2) + iNewScale = iHiRes * 32 - 64 - 1; + if (iNewScale > 0) { - iNewScale = (int)iHiRes * 32 - 64 - 1; // Find the MSB. // - uint ulTmp = rgulRes[iHiRes]; - iNewScale--; + ulTmp = rgulRes[iHiRes]; if ((ulTmp & 0xFFFF0000) == 0) { - ulTmp <<= 16; iNewScale -= 16; + ulTmp <<= 16; } if ((ulTmp & 0xFF000000) == 0) { - ulTmp <<= 8; iNewScale -= 8; + ulTmp <<= 8; } if ((ulTmp & 0xF0000000) == 0) { - ulTmp <<= 4; iNewScale -= 4; + ulTmp <<= 4; } if ((ulTmp & 0xC0000000) == 0) { - ulTmp <<= 2; iNewScale -= 2; + ulTmp <<= 2; + } + if ((ulTmp & 0x80000000) == 0) + { + iNewScale--; + ulTmp <<= 1; } - iNewScale -= (int)ulTmp >> 31; // Multiply bit position by log10(2) to figure it's power of 10. // We scale the log by 256. log(2) = .30103, * 256 = 77. Doing this @@ -614,8 +574,10 @@ PosRem: // current scale of the result, we'll overflow. // if (iNewScale > iScale) - goto ThrowOverflow; + return -1; } + else + iNewScale = 0; // Make sure we scale by enough to bring the current scale factor // into valid range. @@ -630,132 +592,42 @@ PosRem: // be 1 power of 10 short. // iScale -= iNewScale; - uint ulSticky = 0; - uint quotient, remainder = 0; + ulSticky = 0; + sdlTmp.High32 = 0; // initialize remainder for (;;) { - ulSticky |= remainder; // record remainder as sticky bit + ulSticky |= sdlTmp.High32; // record remainder as sticky bit + + if (iNewScale > MaxInt32Scale) + ulPwr = TenToPowerNine; + else + ulPwr = s_powers10[iNewScale]; - uint ulPwr; - uint high = rgulRes[iHiRes]; - // Scaling loop specialized for each power of 10 because division by constant is an order of magnitude faster (especially for 64-bit division that's actually done by 128bit DIV on x64) - switch (iNewScale) + // Compute first quotient. + // DivMod64by32 returns quotient in Lo, remainder in Hi. + // + sdlTmp.int64 = DivMod64by32(rgulRes[iHiRes], ulPwr); + rgulRes[iHiRes] = sdlTmp.Low32; + iCur = iHiRes - 1; + + if (iCur >= 0) { - case 1: - { - const uint power = 10; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 2: - { - const uint power = 100; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 3: - { - const uint power = 1000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 4: - { - const uint power = 10000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 5: - { - const uint power = 100000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 6: - { - const uint power = 1000000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 7: - { - const uint power = 10000000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - case 8: - { - const uint power = 100000000; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } - default: - { - const uint power = TenToPowerNine; - remainder = high - (quotient = high / power) * power; - for (uint i = iHiRes - 1; (int)i >= 0; i--) - { - ulong num = rgulRes[i] + ((ulong)remainder << 32); - remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power; - } - ulPwr = power; - break; - } + // If first quotient was 0, update iHiRes. + // + if (sdlTmp.Low32 == 0) + iHiRes--; + + // Compute subsequent quotients. + // + do + { + sdlTmp.Low32 = rgulRes[iCur]; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr); + rgulRes[iCur] = sdlTmp.Low32; + iCur--; + } while (iCur >= 0); } - rgulRes[iHiRes] = quotient; - // If first quotient was 0, update iHiRes. - // - if (quotient == 0 && iHiRes != 0) - iHiRes--; iNewScale -= MaxInt32Scale; if (iNewScale > 0) @@ -766,8 +638,6 @@ PosRem: // if (iHiRes > 2) { - if (iScale == 0) - goto ThrowOverflow; iNewScale = 1; iScale--; continue; // scale by 10 @@ -777,106 +647,90 @@ PosRem: // If remainder == 1/2 divisor, round up if odd or sticky bit set. // ulPwr >>= 1; // power of 10 always even - if (ulPwr <= remainder && (ulPwr < remainder || ((rgulRes[0] & 1) | ulSticky) != 0) && ++rgulRes[0] == 0) + if (ulPwr <= sdlTmp.High32 && (ulPwr < sdlTmp.High32 || + ((rgulRes[0] & 1) | ulSticky) != 0)) { - uint iCur = 0; - do - { - Debug.Assert(iCur + 1 < bufRes->Length); - } - while (++rgulRes[++iCur] == 0); + iCur = -1; + while (++rgulRes[++iCur] == 0) + ; if (iCur > 2) { // The rounding caused us to carry beyond 96 bits. // Scale by 10 more. // - if (iScale == 0) - goto ThrowOverflow; iHiRes = iCur; ulSticky = 0; // no sticky bit - remainder = 0; // or remainder + sdlTmp.High32 = 0; // or remainder iNewScale = 1; iScale--; continue; // scale by 10 } } - break; + // We may have scaled it more than we planned. Make sure the scale + // factor hasn't gone negative, indicating overflow. + // + if (iScale < 0) + return -1; + + return iScale; } // for(;;) } return iScale; - -ThrowOverflow: - throw new OverflowException(SR.Overflow_Decimal); } // Adjust the quotient to deal with an overflow. We need to divide by 10, // feed in the high bit to undo the overflow and then round as required, - private static int OverflowUnscale(ref Buf12 bufQuo, int iScale, bool fRemainder) + private static void OverflowUnscale(uint[] rgulQuo, bool fRemainder) { - if (--iScale < 0) - throw new OverflowException(SR.Overflow_Decimal); - Split64 sdlTmp = new Split64(); // We have overflown, so load the high bit with a one. sdlTmp.High32 = 1; - sdlTmp.Low32 = bufQuo.U2; + sdlTmp.Low32 = rgulQuo[2]; sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - bufQuo.U2 = sdlTmp.Low32; - sdlTmp.Low32 = bufQuo.U1; + rgulQuo[2] = sdlTmp.Low32; + sdlTmp.Low32 = rgulQuo[1]; sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - bufQuo.U1 = sdlTmp.Low32; - sdlTmp.Low32 = bufQuo.U0; + rgulQuo[1] = sdlTmp.Low32; + sdlTmp.Low32 = rgulQuo[0]; sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - bufQuo.U0 = sdlTmp.Low32; + rgulQuo[0] = sdlTmp.Low32; // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up - if ((sdlTmp.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (bufQuo.U0 & 1) != 0))) - Add32To96(ref bufQuo, 1); - return iScale; + if ((sdlTmp.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (rgulQuo[0] & 1) != 0))) + Add32To96(rgulQuo, 1); } /*** * SearchScale * * Entry: - * bufQuo - 96-bit quotient - * iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX-1 + * ulResHi - Top ULONG of quotient + * ulResMid - Middle ULONG of quotient + * ulResLo - Lower ULONG of quotient + * iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX * * Purpose: * Determine the max power of 10, <= 9, that the quotient can be scaled * up by and still fit in 96 bits. * * Exit: - * Returns power of 10 to scale by. + * Returns power of 10 to scale by, -1 if overflow error. * ***********************************************************************/ - private static int SearchScale(ref Buf12 bufQuo, int iScale) + private static int SearchScale(uint ulResHi, uint ulResMid, uint ulResLo, int iScale) { - const uint OVFL_MAX_9_HI = 4; - const uint OVFL_MAX_8_HI = 42; - const uint OVFL_MAX_7_HI = 429; - const uint OVFL_MAX_6_HI = 4294; - const uint OVFL_MAX_5_HI = 42949; - const uint OVFL_MAX_4_HI = 429496; - const uint OVFL_MAX_3_HI = 4294967; - const uint OVFL_MAX_2_HI = 42949672; - const uint OVFL_MAX_1_HI = 429496729; - const ulong OVFL_MAX_9_MIDLO = 5441186219426131129; - - uint ulResHi = bufQuo.U2; - ulong ulResMidLo = bufQuo.Low64; - int iCurScale = 0; + int iCurScale; // Quick check to stop us from trying to scale any more. // - if (ulResHi > OVFL_MAX_1_HI) + if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX) { + iCurScale = 0; goto HaveScale; } - var powerOvfl = PowerOvflValues; if (iScale > DEC_SCALE_MAX - 9) { // We can't scale by 10^9 without exceeding the max scale factor. @@ -884,91 +738,120 @@ ThrowOverflow: // standard search for scale factor. // iCurScale = DEC_SCALE_MAX - iScale; - if (ulResHi < powerOvfl[iCurScale - 1].Hi) + if (ulResHi < PowerOvfl.Hi(iCurScale - 1)) goto HaveScale; + + if (ulResHi == PowerOvfl.Hi(iCurScale - 1)) + { + UpperEq(ulResMid, ulResLo, ref iCurScale); + goto HaveScale; + } } - else if (ulResHi < OVFL_MAX_9_HI || ulResHi == OVFL_MAX_9_HI && ulResMidLo <= OVFL_MAX_9_MIDLO) + else if (ulResHi < OVFL_MAX_9_HI || (ulResHi == OVFL_MAX_9_HI && + ulResMid < OVFL_MAX_9_MID) || (ulResHi == OVFL_MAX_9_HI && ulResMid == OVFL_MAX_9_MID && ulResLo <= OVFL_MAX_9_LO)) return 9; - // Search for a power to scale by < 9. Do a binary search. + // Search for a power to scale by < 9. Do a binary search + // on PowerOvfl[]. // - if (ulResHi > OVFL_MAX_5_HI) + iCurScale = 5; + if (ulResHi < OVFL_MAX_5_HI) + iCurScale = 7; + else if (ulResHi > OVFL_MAX_5_HI) + iCurScale = 3; + else { - if (ulResHi > OVFL_MAX_3_HI) - { - iCurScale = 2; - if (ulResHi > OVFL_MAX_2_HI) - iCurScale--; - } - else - { - iCurScale = 4; - if (ulResHi > OVFL_MAX_4_HI) - iCurScale--; - } + UpperEq(ulResMid, ulResLo, ref iCurScale); + goto HaveScale; } + + // iCurScale is 3 or 7. + // + if (ulResHi < PowerOvfl.Hi(iCurScale - 1)) + iCurScale++; + else if (ulResHi > PowerOvfl.Hi(iCurScale - 1)) + iCurScale--; else { - if (ulResHi > OVFL_MAX_7_HI) - { - iCurScale = 6; - if (ulResHi > OVFL_MAX_6_HI) - iCurScale--; - } - else - { - iCurScale = 8; - if (ulResHi > OVFL_MAX_8_HI) - iCurScale--; - } + UpperEq(ulResMid, ulResLo, ref iCurScale); + goto HaveScale; } + // iCurScale is 2, 4, 6, or 8. + // // In all cases, we already found we could not use the power one larger. // So if we can use this power, it is the biggest, and we're done. If // we can't use this power, the one below it is correct for all cases // unless it's 10^1 -- we might have to go to 10^0 (no scaling). // - if (ulResHi == powerOvfl[iCurScale - 1].Hi && ulResMidLo > powerOvfl[iCurScale - 1].MidLo) + if (ulResHi > PowerOvfl.Hi(iCurScale - 1)) iCurScale--; + if (ulResHi == PowerOvfl.Hi(iCurScale - 1)) + UpperEq(ulResMid, ulResLo, ref iCurScale); + HaveScale: // iCurScale = largest power of 10 we can scale by without overflow, // iCurScale < 9. See if this is enough to make scale factor // positive if it isn't already. // if (iCurScale + iScale < 0) - throw new OverflowException(SR.Overflow_Decimal); + iCurScale = -1; return iCurScale; } + private static void UpperEq(uint ulResMid, uint ulResLo, ref int iCurScale) + { + if (ulResMid > PowerOvfl.Mid(iCurScale - 1) || + (ulResMid == PowerOvfl.Mid(iCurScale - 1) && ulResLo > PowerOvfl.Lo(iCurScale - 1))) + { + iCurScale--; + } + } + // Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer // Returns false if there is an overflow - private static bool Add32To96(ref Buf12 bufNum, uint ulValue) + private static bool Add32To96(uint[] rgulNum, uint ulValue) { - if ((bufNum.Low64 += ulValue) < ulValue) + rgulNum[0] += ulValue; + if (rgulNum[0] < ulValue) { - if (++bufNum.U2 == 0) - return false; + if (++rgulNum[1] == 0) + { + if (++rgulNum[2] == 0) + return false; + } } return true; } // DecAddSub adds or subtracts two decimal values. On return, d1 contains the result // of the operation. Passing in true for bSign means subtract and false means add. - private static unsafe void DecAddSub(ref Decimal d1, ref Decimal d2, bool bSign) + // + // Returns true if we overflow otherwise false. + private static bool DecAddSub(ref Decimal d1, ref Decimal d2, bool bSign) { - ulong low64 = d1.Low64; - uint high = d1.High, flags = d1.uflags, d2flags = d2.uflags; + uint[] rgulNum = new uint[6]; + uint ulPwr; + int iScale; + int iHiProd; + int iCur; + Split64 sdlTmp = new Split64(); + Decimal result = new Decimal(); + Decimal tmp = new Decimal(); - uint xorflags = d2flags ^ flags; - bSign ^= (xorflags & SignMask) != 0; + bSign ^= d2.IsNegative ^ d1.IsNegative; - if ((xorflags & ScaleMask) == 0) + if (d2.Scale == d1.Scale) { // Scale factors are equal, no alignment necessary. // - goto AlignedAdd; + result.IsNegative = d1.IsNegative; + result.Scale = d1.Scale; + + if (AlignedAdd(ref result, ref d1, ref d2, bSign)) + return true; } else { @@ -978,28 +861,22 @@ ThrowOverflow: // the larger scale factor. The result will have the larger // scale factor. // - uint d1flags = flags; - flags = d2flags & ScaleMask | flags & SignMask; // scale factor of "smaller", but sign of "larger" - int iScale = (int)(flags - d1flags) >> ScaleShift; + result.Scale = d2.Scale; // scale factor of "smaller" + result.IsNegative = d1.IsNegative; // but sign of "larger" + iScale = result.Scale - d1.Scale; if (iScale < 0) { // Guessed scale factor wrong. Swap operands. // iScale = -iScale; - flags = d1flags; - if (bSign) - flags ^= SignMask; - low64 = d2.Low64; - high = d2.High; + result.Scale = d1.Scale; + result.IsNegative ^= bSign; + tmp = d2; d2 = d1; + d1 = tmp; } - Buf24 bufNum; - _ = &bufNum; // workaround for CS0165 - uint ulPwr, iHiProd; - ulong tmp64; - // d1 will need to be multiplied by 10^iScale so // it will have the same scale as d2. We could be // extending it to up to 192 bits of precision. @@ -1009,268 +886,302 @@ ThrowOverflow: // Scaling won't make it larger than 4 ULONGs // ulPwr = s_powers10[iScale]; - ulong tmpLow = UInt32x32To64((uint)low64, ulPwr); - tmp64 = (low64 >> 32) * ulPwr + (tmpLow >> 32); - low64 = (uint)tmpLow + (tmp64 << 32); - tmp64 >>= 32; - tmp64 += UInt32x32To64(high, ulPwr); - if (tmp64 <= uint.MaxValue) + tmp = UInt32x32To64(d1.Low, ulPwr); + sdlTmp.int64 = UInt32x32To64(d1.Mid, ulPwr); + sdlTmp.int64 += tmp.Mid; + tmp.Mid = sdlTmp.Low32; + tmp.High = sdlTmp.High32; + sdlTmp.int64 = UInt32x32To64(d1.High, ulPwr); + sdlTmp.int64 += tmp.High; + if (sdlTmp.High32 == 0) { // Result fits in 96 bits. Use standard aligned add. // - high = (uint)tmp64; - goto AlignedAdd; + tmp.High = sdlTmp.Low32; + d1 = tmp; + if (AlignedAdd(ref result, ref d1, ref d2, bSign)) + return true; + d1 = result; + return false; } - bufNum.Low64 = low64; - bufNum.Mid64 = tmp64; + rgulNum[0] = tmp.Low; + rgulNum[1] = tmp.Mid; + rgulNum[2] = sdlTmp.Low32; + rgulNum[3] = sdlTmp.High32; iHiProd = 3; } else { + // Have to scale by a bunch. Move the number to a buffer + // where it has room to grow as it's scaled. + // + rgulNum[0] = d1.Low; + rgulNum[1] = d1.Mid; + rgulNum[2] = d1.High; iHiProd = 2; // Scan for zeros in the upper words. // - if (high == 0) + if (rgulNum[2] == 0) { iHiProd = 1; - if (low64 >> 32 == 0) + if (rgulNum[1] == 0) { iHiProd = 0; - if ((uint)low64 == 0) + if (rgulNum[0] == 0) { // Left arg is zero, return right. // - uint signFlags = flags & SignMask; - if (bSign) - signFlags ^= SignMask; - d1 = d2; - d1.uflags = d2.uflags & ScaleMask | signFlags; - return; + result.Low64 = d2.Low64; + result.High = d2.High; + result.IsNegative ^= bSign; + d1 = result; + return false; } } } - // Have to scale by a bunch. Move the number to a buffer - // where it has room to grow as it's scaled. - // - bufNum.Low64 = low64; - bufNum.U2 = high; - // Scaling loop, up to 10^9 at a time. iHiProd stays updated // with index of highest non-zero ULONG. // for (; iScale > 0; iScale -= MaxInt32Scale) { - if (iScale >= MaxInt32Scale) + if (iScale > MaxInt32Scale) ulPwr = TenToPowerNine; else ulPwr = s_powers10[iScale]; - tmp64 = 0; - uint* rgulNum = (uint*)&bufNum; - for (uint iCur = 0; ;) + sdlTmp.High32 = 0; + for (iCur = 0; iCur <= iHiProd; iCur++) { - Debug.Assert(iCur < bufNum.Length); - tmp64 += UInt32x32To64(rgulNum[iCur], ulPwr); - rgulNum[iCur] = (uint)tmp64; - iCur++; - tmp64 >>= 32; - if (iCur > iHiProd) - break; + sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.High32; + rgulNum[iCur] = sdlTmp.Low32; } - if ((uint)tmp64 != 0) - { + if (sdlTmp.High32 != 0) // We're extending the result by another ULONG. - Debug.Assert(iHiProd + 1 < bufNum.Length); - rgulNum[++iHiProd] = (uint)tmp64; - } + rgulNum[++iHiProd] = sdlTmp.High32; } } // Scaling complete, do the add. Could be subtract if signs differ. // - tmp64 = bufNum.Low64; - low64 = d2.Low64; - uint tmpHigh = bufNum.U2; - high = d2.High; + sdlTmp.Low32 = rgulNum[0]; + sdlTmp.High32 = rgulNum[1]; if (bSign) { // Signs differ, subtract. // - low64 = tmp64 - low64; - high = tmpHigh - high; + result.Low64 = sdlTmp.int64 - d2.Low64; + result.High = rgulNum[2] - d2.High; // Propagate carry // - if (low64 > tmp64) + if (result.Low64 > sdlTmp.int64) { - high--; - if (high < tmpHigh) - goto NoCarry; + result.High--; + if (result.High >= rgulNum[2]) + if (LongSub(ref result, ref iHiProd, rgulNum)) + { + d1 = result; + return false; + } } - else if (high <= tmpHigh) - goto NoCarry; - - // If rgulNum has more than 96 bits of precision, then we need to - // carry the subtraction into the higher bits. If it doesn't, - // then we subtracted in the wrong order and have to flip the - // sign of the result. - // - if (iHiProd <= 2) + else if (result.High > rgulNum[2]) { - goto SignFlip; + if (LongSub(ref result, ref iHiProd, rgulNum)) + { + d1 = result; + return false; + } } - - uint* rgulNum = (uint*)&bufNum; - uint iCur = 3; - do - { - Debug.Assert(iCur < bufNum.Length); - } while (rgulNum[iCur++]-- == 0); - Debug.Assert(iHiProd < bufNum.Length); - if (rgulNum[iHiProd] == 0) - iHiProd--; } else { // Signs the same, add. // - low64 += tmp64; - high += tmpHigh; + result.Low64 = sdlTmp.int64 + d2.Low64; + result.High = rgulNum[2] + d2.High; // Propagate carry // - if (low64 < tmp64) + if (result.Low64 < sdlTmp.int64) { - high++; - if (high > tmpHigh) - goto NoCarry; + result.High++; + if (result.High <= rgulNum[2]) + LongAdd(ref iHiProd, rgulNum); } - else if (high >= tmpHigh) - goto NoCarry; - - uint* rgulNum = (uint*)&bufNum; - uint iCur = 3; - do + else if (result.High < rgulNum[2]) { - Debug.Assert(iCur < bufNum.Length); - if (iHiProd < iCur) - { - rgulNum[iCur] = 1; - iHiProd = iCur; - break; - } - } while (++rgulNum[iCur++] == 0); + LongAdd(ref iHiProd, rgulNum); + } } -NoCarry: if (iHiProd > 2) { - bufNum.Low64 = low64; - bufNum.U2 = high; - int scale = ScaleResult(&bufNum, iHiProd, (byte)(flags >> ScaleShift)); - flags = (flags & ~ScaleMask) | ((uint)scale << ScaleShift); - - low64 = bufNum.Low64; - high = bufNum.U2; + rgulNum[0] = result.Low; + rgulNum[1] = result.Mid; + rgulNum[2] = result.High; + int scale = ScaleResult(rgulNum, iHiProd, result.Scale); + if (scale == -1) + return true; + result.Scale = scale; + + result.Low = rgulNum[0]; + result.Mid = rgulNum[1]; + result.High = rgulNum[2]; } + } + + d1 = result; + return false; + } + + private static void SignFlip(ref Decimal value) + { + value.Low64 = (ulong)-(long)value.Low64; + value.High = ~value.High; + if (value.Low64 == 0) + value.High++; + value.IsNegative ^= true; + } + + // Returns true if we overflowed + private static bool AlignedScale(ref Decimal value) + { + Split64 sdlTmp = new Split64(); + + // Divide the value by 10, dropping the scale factor. + // + if (value.Scale == 0) + return true; + value.Scale--; - goto ReturnResult; + sdlTmp.Low32 = value.High; + sdlTmp.High32 = 1; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + value.High = sdlTmp.Low32; + + sdlTmp.Low32 = value.Mid; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + value.Mid = sdlTmp.Low32; + + sdlTmp.Low32 = value.Low; + sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); + value.Low = sdlTmp.Low32; + + // See if we need to round up. + // + if (sdlTmp.High32 >= 5 && (sdlTmp.High32 > 5 || (value.Low & 1) != 0)) + { + value.Low64 = value.Low64 + 1; + if (value.Low64 == 0) + value.High++; } + return false; + } -SignFlip: + private static bool LongSub(ref Decimal value, ref int iHiProd, uint[] rgulNum) + { + // If rgulNum has more than 96 bits of precision, then we need to + // carry the subtraction into the higher bits. If it doesn't, + // then we subtracted in the wrong order and have to flip the + // sign of the result. + // + if (iHiProd <= 2) { - // Got negative result. Flip its sign. - flags ^= SignMask; - high = ~high; - low64 = (ulong)-(long)low64; - if (low64 == 0) - high++; - goto ReturnResult; + SignFlip(ref value); + return true; } -AlignedScale: + int iCur = 3; + while (rgulNum[iCur++]-- == 0) + ; + if (rgulNum[iHiProd] == 0) + iHiProd--; + + return false; + } + + private static void LongAdd(ref int iHiProd, uint[] rgulNum) + { + int iCur = 3; + do { - // The addition carried above 96 bits. - // Divide the value by 10, dropping the scale factor. - // - if ((flags & ScaleMask) == 0) - throw new OverflowException(SR.Overflow_Decimal); - flags -= 1 << ScaleShift; - - const uint den = 10; - ulong num = high + (1UL << 32); - high = (uint)(num / den); - num = ((num - high * den) << 32) + (low64 >> 32); - uint div = (uint)(num / den); - num = ((num - div * den) << 32) + (uint)low64; - low64 = div; - low64 <<= 32; - div = (uint)(num / den); - low64 += div; - div = (uint)num - div * den; - - // See if we need to round up. - // - if (div >= 5 && (div > 5 || (low64 & 1) != 0)) + if (iHiProd < iCur) { - if (++low64 == 0) - high++; + rgulNum[iCur] = 1; + iHiProd = iCur; + break; } - goto ReturnResult; - } + } while (++rgulNum[iCur++] == 0); + } -AlignedAdd: + // Returns true if we overflowed + private static bool AlignedAdd(ref Decimal value, ref Decimal d1, ref Decimal d2, bool bSign) + { + if (bSign) { - ulong d1Low64 = low64; - uint d1High = high; - if (bSign) - { - // Signs differ - subtract - // - low64 = d1Low64 - d2.Low64; - high = d1High - d2.High; + // Signs differ - subtract + // + value.Low64 = d1.Low64 - d2.Low64; + value.High = d1.High - d2.High; - // Propagate carry - // - if (low64 > d1Low64) - { - high--; - if (high >= d1High) - goto SignFlip; - } - else if (high > d1High) - goto SignFlip; + // Propagate carry + // + if (value.Low64 > d1.Low64) + { + value.High--; + if (value.High >= d1.High) + SignFlip(ref value); } - else + else if (value.High > d1.High) { - // Signs are the same - add + // Got negative result. Flip its sign. // - low64 = d1Low64 + d2.Low64; - high = d1High + d2.High; + SignFlip(ref value); + } + } + else + { + // Signs are the same - add + // + value.Low64 = d1.Low64 + d2.Low64; + value.High = d1.High + d2.High; - // Propagate carry - // - if (low64 < d1Low64) + // Propagate carry + // + if (value.Low64 < d1.Low64) + { + value.High++; + if (value.High <= d1.High) { - high++; - if (high <= d1High) - goto AlignedScale; + if (AlignedScale(ref value)) + return true; } - else if (high < d1High) - goto AlignedScale; } - goto ReturnResult; + else if (value.High < d1.High) + { + // The addition carried above 96 bits. Divide the result by 10, + // dropping the scale factor. + // + if (AlignedScale(ref value)) + return true; + } } + return false; + } -ReturnResult: - d1.uflags = flags; - d1.High = high; - d1.Low64 = low64; - return; + private static void RoundUp(uint[] rgulQuo, ref int iScale) + { + if (!Add32To96(rgulQuo, 1)) + { + if (iScale == 0) + throw new OverflowException(SR.Overflow_Decimal); + iScale--; + OverflowUnscale(rgulQuo, true); + } } // Returns the absolute value of the given Decimal. If d is @@ -1299,19 +1210,18 @@ ReturnResult: * None. * ***********************************************************************/ - private static unsafe uint DecFixInt(ref Decimal input, ref Decimal result) + private static uint DecFixInt(ref Decimal input, ref Decimal result) { - Buf12 bufNum; - _ = &bufNum; // workaround for CS0165 + uint[] tmpNum = new uint[3]; uint remainder; uint power; int scale; if (input.Scale > 0) { - bufNum.U0 = input.ulo; - bufNum.U1 = input.umid; - bufNum.U2 = input.uhi; + tmpNum[0] = input.ulo; + tmpNum[1] = input.umid; + tmpNum[2] = input.uhi; scale = input.Scale; result.IsNegative = input.IsNegative; remainder = 0; @@ -1323,13 +1233,13 @@ ReturnResult: else power = s_powers10[scale]; - remainder |= Div96By32(ref bufNum, power); + remainder |= Div96By32(tmpNum, power); scale -= MaxInt32Scale; } while (scale > 0); - result.ulo = bufNum.U0; - result.umid = bufNum.U1; - result.uhi = bufNum.U2; + result.ulo = tmpNum[0]; + result.umid = tmpNum[1]; + result.uhi = tmpNum[2]; result.Scale = 0; return remainder; @@ -1482,120 +1392,101 @@ ReturnResult: //********************************************************************** internal static int VarDecCmp(ref Decimal pdecL, ref Decimal pdecR) { - if ((pdecR.Low | pdecR.Mid | pdecR.High) == 0) - { - if ((pdecL.Low | pdecL.Mid | pdecL.High) == 0) - return 0; - return (pdecL.flags >> 31) | 1; - } - if ((pdecL.Low | pdecL.Mid | pdecL.High) == 0) - return -((pdecR.flags >> 31) | 1); - - int sign = (pdecL.flags >> 31) - (pdecR.flags >> 31); - if (sign != 0) - return sign; - return VarDecCmpSub(ref pdecL, ref pdecR); - } + int signLeft = 0; + int signRight = 0; - private static int VarDecCmpSub(ref Decimal d1, ref Decimal d2) - { - int flags = d2.flags; - int sign = (flags >> 31) | 1; - int iScale = flags - d1.flags; + if (pdecL.Low != 0 || pdecL.Mid != 0 || pdecL.High != 0) + signLeft = pdecL.IsNegative ? -1 : 1; - ulong low64 = d1.Low64; - uint high = d1.High; + if (pdecR.Low != 0 || pdecR.Mid != 0 || pdecR.High != 0) + signRight = pdecR.IsNegative ? -1 : 1; - if (iScale != 0) + if (signLeft == signRight) { - iScale >>= ScaleShift; - - // Scale factors are not equal. Assume that a larger scale factor (more decimal places) is likely to mean that number is smaller. - // Start by guessing that the right operand has the larger scale factor. - if (iScale < 0) - { - // Guessed scale factor wrong. Swap operands. - iScale = -iScale; - sign = -sign; - low64 = d2.Low64; - high = d2.High; - d2 = d1; - } - - // d1 will need to be multiplied by 10^iScale so it will have the same scale as d2. - // Scaling loop, up to 10^9 at a time. - do - { - uint ulPwr = iScale >= MaxInt32Scale ? TenToPowerNine : s_powers10[iScale]; - ulong tmpLow = UInt32x32To64((uint)low64, ulPwr); - ulong tmp = (low64 >> 32) * ulPwr + (tmpLow >> 32); - low64 = (uint)tmpLow + (tmp << 32); - tmp >>= 32; - tmp += UInt32x32To64(high, ulPwr); - // If the scaled value has more than 96 significant bits then it's greater than d2 - if (tmp > uint.MaxValue) - return sign; - high = (uint)tmp; - } while ((iScale -= MaxInt32Scale) > 0); - } + if (signLeft == 0) // both are zero + return 0; // return equal - uint cmpHigh = high - d2.High; - if (cmpHigh != 0) - { - // check for overflow - if (cmpHigh > high) - sign = -sign; - return sign; + Decimal decLAndResult = pdecL; // Copy the left and pass that to AddSub because it gets mutated. + DecAddSub(ref decLAndResult, ref pdecR, true); // Call DecAddSub instead of VarDecSub to avoid exceptions + if (decLAndResult.Low == 0 && decLAndResult.Mid == 0 && decLAndResult.High == 0) + return 0; + if (decLAndResult.IsNegative) + return -1; + return 1; } - ulong cmpLow64 = low64 - d2.Low64; - if (cmpLow64 == 0) - sign = 0; - // check for overflow - else if (cmpLow64 > low64) - sign = -sign; - return sign; + // Signs are different. Used signed byte compares + // + if (signLeft > signRight) + return 1; + return -1; } //********************************************************************** // VarDecMul - Decimal Multiply //********************************************************************** - internal static unsafe void VarDecMul(ref Decimal pdecL, ref Decimal pdecR) + internal static void VarDecMul(ref Decimal pdecL, ref Decimal pdecR, out Decimal pdecRes) { - int iScale = (byte)(pdecL.uflags + pdecR.uflags >> ScaleShift); + Split64 sdlTmp = new Split64(); + Split64 sdlTmp2 = new Split64(); + Split64 sdlTmp3 = new Split64(); + int iScale; + int iHiProd; + uint ulPwr; + uint ulRemLo; + uint ulRemHi; + uint[] rgulProd = new uint[6]; + + pdecRes = new Decimal(); + iScale = pdecL.Scale + pdecR.Scale; if ((pdecL.High | pdecL.Mid | pdecR.High | pdecR.Mid) == 0) { // Upper 64 bits are zero. // - ulong low64 = UInt32x32To64(pdecL.Low, pdecR.Low); + sdlTmp.int64 = UInt32x32To64(pdecL.Low, pdecR.Low); if (iScale > DEC_SCALE_MAX) { // Result iScale is too big. Divide result by power of 10 to reduce it. // If the amount to divide by is > 19 the result is guaranteed // less than 1/2. [max value in 64 bits = 1.84E19] // - iScale -= DEC_SCALE_MAX + 1; - if (iScale >= MaxInt64Scale) - goto ReturnZero; - - ulong ulPwr = s_ulongPowers10[iScale]; + iScale -= DEC_SCALE_MAX; + if (iScale > 19) + { + //DECIMAL_SETZERO(*pdecRes); + return; + } + if (iScale > MaxInt32Scale) + { + // Divide by 1E10 first, to get the power down to a 32-bit quantity. + // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now + // then multiply the next divisor by 4 (which will be a max of 4E9). + // + ulRemLo = FullDiv64By32(ref sdlTmp.int64, TenToPowerTenDiv4); + ulPwr = s_powers10[iScale - 10] << 2; + } + else + { + ulPwr = s_powers10[iScale]; + ulRemLo = 0; + } - // TODO: https://github.com/dotnet/coreclr/issues/3439 - ulong div = low64 / ulPwr; - ulong remainder = low64 - div * ulPwr; - low64 = div; + // Power to divide by fits in 32 bits. + // + ulRemHi = FullDiv64By32(ref sdlTmp.int64, ulPwr); // Round result. See if remainder >= 1/2 of divisor. // Divisor is a power of 10, so it is always even. // ulPwr >>= 1; - if (remainder >= ulPwr && (remainder > ulPwr || (low64 & 1) > 0)) - low64++; + if (ulRemHi >= ulPwr && (ulRemHi > ulPwr || (ulRemLo | (sdlTmp.Low32 & 1)) > 0)) + sdlTmp.int64++; iScale = DEC_SCALE_MAX; } - pdecL.Low64 = low64; + pdecRes.Low64 = sdlTmp.int64; + pdecRes.High = 0; } else { @@ -1620,93 +1511,87 @@ ReturnResult: // ------------------------------ // [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array // - uint iHiProd; - Buf24 bufProd; - _ = &bufProd; // workaround for CS0165 + sdlTmp.int64 = UInt32x32To64(pdecL.Low, pdecR.Low); + rgulProd[0] = sdlTmp.Low32; - ulong tmp = UInt32x32To64(pdecL.Low, pdecR.Low); - bufProd.U0 = (uint)tmp; + sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.Mid) + sdlTmp.High32; - ulong tmp2 = UInt32x32To64(pdecL.Low, pdecR.Mid) + (tmp >> 32); - - tmp = UInt32x32To64(pdecL.Mid, pdecR.Low); - tmp += tmp2; // this could generate carry - bufProd.U1 = (uint)tmp; - if (tmp < tmp2) // detect carry - tmp2 = 1UL << 32; + sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Low); + sdlTmp.int64 += sdlTmp2.int64; // this could generate carry + rgulProd[1] = sdlTmp.Low32; + if (sdlTmp.int64 < sdlTmp2.int64) // detect carry + sdlTmp2.High32 = 1; else - tmp2 = 0; - tmp2 += tmp >> 32; + sdlTmp2.High32 = 0; + sdlTmp2.Low32 = sdlTmp.High32; - tmp = UInt32x32To64(pdecL.Mid, pdecR.Mid) + tmp2; + sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Mid) + sdlTmp2.int64; if ((pdecL.High | pdecR.High) > 0) { // Highest 32 bits is non-zero. Calculate 5 more partial products. // - tmp2 = UInt32x32To64(pdecL.Low, pdecR.High); - tmp += tmp2; // this could generate carry - ulong tmp3 = 0; - if (tmp < tmp2) // detect carry - tmp3 = 1UL << 32; - - tmp2 = UInt32x32To64(pdecL.High, pdecR.Low); - tmp += tmp2; // this could generate carry - bufProd.U2 = (uint)tmp; - if (tmp < tmp2) // detect carry - tmp3 += 1UL << 32; - tmp2 = tmp3 + (tmp >> 32); - - tmp = UInt32x32To64(pdecL.Mid, pdecR.High); - tmp += tmp2; // this could generate carry - tmp3 = 0; - if (tmp < tmp2) // detect carry - tmp3 = 1UL << 32; - - tmp2 = UInt32x32To64(pdecL.High, pdecR.Mid); - tmp += tmp2; // this could generate carry - bufProd.U3 = (uint)tmp; - if (tmp < tmp2) // detect carry - tmp3 += 1UL << 32; - tmp3 += tmp >> 32; - - tmp = UInt32x32To64(pdecL.High, pdecR.High) + tmp3; - bufProd.High64 = tmp; + sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.High); + sdlTmp.int64 += sdlTmp2.int64; // this could generate carry + if (sdlTmp.int64 < sdlTmp2.int64) // detect carry + sdlTmp3.High32 = 1; + else + sdlTmp3.High32 = 0; + + sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Low); + sdlTmp.int64 += sdlTmp2.int64; // this could generate carry + rgulProd[2] = sdlTmp.Low32; + if (sdlTmp.int64 < sdlTmp2.int64) // detect carry + sdlTmp3.High32++; + sdlTmp3.Low32 = sdlTmp.High32; + + sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.High); + sdlTmp.int64 += sdlTmp3.int64; // this could generate carry + if (sdlTmp.int64 < sdlTmp3.int64) // detect carry + sdlTmp3.High32 = 1; + else + sdlTmp3.High32 = 0; + + sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Mid); + sdlTmp.int64 += sdlTmp2.int64; // this could generate carry + rgulProd[3] = sdlTmp.Low32; + if (sdlTmp.int64 < sdlTmp2.int64) // detect carry + sdlTmp3.High32++; + sdlTmp3.Low32 = sdlTmp.High32; + + sdlTmp.int64 = UInt32x32To64(pdecL.High, pdecR.High) + sdlTmp3.int64; + rgulProd[4] = sdlTmp.Low32; + rgulProd[5] = sdlTmp.High32; iHiProd = 5; } - else if (tmp != 0) + else { - bufProd.Mid64 = tmp; + rgulProd[2] = sdlTmp.Low32; + rgulProd[3] = sdlTmp.High32; iHiProd = 3; } - else - iHiProd = 1; // Check for leading zero ULONGs on the product // - uint* rgulProd = (uint*)&bufProd; while (rgulProd[iHiProd] == 0) { - if (iHiProd == 0) - goto ReturnZero; iHiProd--; + if (iHiProd < 0) + return; } - if (iHiProd > 2 || iScale > DEC_SCALE_MAX) - { - iScale = ScaleResult(&bufProd, iHiProd, iScale); - } + iScale = ScaleResult(rgulProd, iHiProd, iScale); + if (iScale == -1) + throw new OverflowException(SR.Overflow_Decimal); - pdecL.Low64 = bufProd.Low64; - pdecL.High = bufProd.U2; + pdecRes.Low = rgulProd[0]; + pdecRes.Mid = rgulProd[1]; + pdecRes.High = rgulProd[2]; } - pdecL.uflags = ((pdecR.uflags ^ pdecL.uflags) & SignMask) | ((uint)iScale << ScaleShift); - return; - -ReturnZero: - pdecL = default(Decimal); + pdecRes.IsNegative = pdecR.IsNegative ^ pdecL.IsNegative; + pdecRes.Scale = (char)iScale; } //********************************************************************** @@ -2048,45 +1933,56 @@ ReturnZero: // of the operation internal static void VarDecAdd(ref Decimal d1, ref Decimal d2) { - DecAddSub(ref d1, ref d2, false); + if (DecAddSub(ref d1, ref d2, false)) + throw new OverflowException(SR.Overflow_Decimal); } // VarDecSub divides two decimal values. On return, d1 contains the result // of the operation. internal static void VarDecSub(ref Decimal d1, ref Decimal d2) { - DecAddSub(ref d1, ref d2, true); + if (DecAddSub(ref d1, ref d2, true)) + throw new OverflowException(SR.Overflow_Decimal); } // VarDecDiv divides two decimal values. On return, d1 contains the result // of the operation. - internal static unsafe void VarDecDiv(ref Decimal d1, ref Decimal d2) + internal static void VarDecDiv(ref Decimal d1, ref Decimal d2) { - Buf12 bufQuo, bufDivisor; - _ = &bufQuo; // workaround for CS0165 - _ = &bufDivisor; // workaround for CS0165 + uint[] rgulQuo = new uint[3]; + uint[] rgulQuoSave = new uint[3]; + uint[] rgulRem = new uint[4]; + uint[] rgulDivisor = new uint[3]; uint ulPwr; + uint ulTmp; + uint ulTmp1; + Split64 sdlTmp = new Split64(); + Split64 sdlDivisor = new Split64(); + int iScale; int iCurScale; + bool fUnscale; - int iScale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift); - bool fUnscale = false; - uint ulTmp; + iScale = d1.Scale - d2.Scale; + fUnscale = false; + rgulDivisor[0] = d2.Low; + rgulDivisor[1] = d2.Mid; + rgulDivisor[2] = d2.High; - if (d2.High == 0 && d2.Mid == 0) + if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) { // Divisor is only 32 bits. Easy divide. // - uint den = d2.Low; - if (den == 0) + if (rgulDivisor[0] == 0) throw new DivideByZeroException(SR.Overflow_Decimal); - bufQuo.Low64 = d1.Low64; - bufQuo.U2 = d1.High; - uint remainder = Div96By32(ref bufQuo, den); + rgulQuo[0] = d1.Low; + rgulQuo[1] = d1.Mid; + rgulQuo[2] = d1.High; + rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]); for (;;) { - if (remainder == 0) + if (rgulRem[0] == 0) { if (iScale < 0) { @@ -2116,32 +2012,38 @@ ReturnZero: // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is // assured not to overflow. // - if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); + if (iCurScale == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // - ulTmp = remainder << 1; - if (ulTmp < remainder || ulTmp >= den && (ulTmp > den || (bufQuo.U0 & 1) != 0)) - goto RoundUp; + ulTmp = rgulRem[0] << 1; + if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] && + (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1) != 0))) + RoundUp(rgulQuo, ref iScale); break; } + if (iCurScale < 0) + throw new OverflowException(SR.Overflow_Decimal); + HaveScale: ulPwr = s_powers10[iCurScale]; iScale += iCurScale; - if (IncreaseScale(ref bufQuo, ulPwr) != 0) - goto ThrowOverflow; + if (IncreaseScale(rgulQuo, ulPwr) != 0) + throw new OverflowException(SR.Overflow_Decimal); - ulong num = UInt32x32To64(remainder, ulPwr); - // TODO: https://github.com/dotnet/coreclr/issues/3439 - uint div = (uint)(num / den); - remainder = (uint)num - div * den; + sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]); + rgulRem[0] = sdlTmp.High32; - if (!Add32To96(ref bufQuo, div)) + if (!Add32To96(rgulQuo, sdlTmp.Low32)) { - iScale = OverflowUnscale(ref bufQuo, iScale, remainder != 0); + if (iScale == 0) + throw new OverflowException(SR.Overflow_Decimal); + iScale--; + OverflowUnscale(rgulQuo, (rgulRem[0] != 0)); break; } } // for (;;) @@ -2155,56 +2057,68 @@ ReturnZero: // normalize by. The dividend will be shifted by the same amount so // the quotient is not changed. // - bufDivisor.Low64 = d2.Low64; - ulTmp = d2.High; - bufDivisor.U2 = ulTmp; - if (ulTmp == 0) - ulTmp = d2.Mid; + if (rgulDivisor[2] == 0) + ulTmp = rgulDivisor[1]; + else + ulTmp = rgulDivisor[2]; - iCurScale = 1; + iCurScale = 0; if ((ulTmp & 0xFFFF0000) == 0) { - ulTmp <<= 16; iCurScale += 16; + ulTmp <<= 16; } if ((ulTmp & 0xFF000000) == 0) { - ulTmp <<= 8; iCurScale += 8; + ulTmp <<= 8; } if ((ulTmp & 0xF0000000) == 0) { - ulTmp <<= 4; iCurScale += 4; + ulTmp <<= 4; } if ((ulTmp & 0xC0000000) == 0) { - ulTmp <<= 2; iCurScale += 2; + ulTmp <<= 2; + } + if ((ulTmp & 0x80000000) == 0) + { + iCurScale++; + ulTmp <<= 1; } - iCurScale += (int)ulTmp >> 31; // Shift both dividend and divisor left by iCurScale. // - Buf16 bufRem; - _ = &bufRem; // workaround for CS0165 - bufRem.Low64 = d1.Low64 << iCurScale; - bufRem.High64 = (d1.Mid + ((ulong)d1.High << 32)) >> (31 - iCurScale) >> 1; - - ulong divisor = bufDivisor.Low64 << iCurScale; - - if (bufDivisor.U2 == 0) + sdlTmp.int64 = d1.Low64 << iCurScale; + rgulRem[0] = sdlTmp.Low32; + rgulRem[1] = sdlTmp.High32; + sdlTmp.Low32 = d1.Mid; + sdlTmp.High32 = d1.High; + sdlTmp.int64 <<= iCurScale; + rgulRem[2] = sdlTmp.High32; + rgulRem[3] = (d1.High >> (31 - iCurScale)) >> 1; + + sdlDivisor.Low32 = rgulDivisor[0]; + sdlDivisor.High32 = rgulDivisor[1]; + sdlDivisor.int64 <<= iCurScale; + + if (rgulDivisor[2] == 0) { // Have a 64-bit divisor in sdlDivisor. The remainder // (currently 96 bits spread over 4 ULONGs) will be < divisor. // + sdlTmp.Low32 = rgulRem[2]; + sdlTmp.High32 = rgulRem[3]; - bufQuo.U1 = Div96By64(ref *(Buf12*)&bufRem.U1, divisor); - bufQuo.U0 = Div96By64(ref *(Buf12*)&bufRem, divisor); + rgulQuo[2] = 0; + rgulQuo[1] = Div96By64(new ArraySegment<uint>(rgulRem, 1, 3), sdlDivisor); + rgulQuo[0] = Div96By64(rgulRem, sdlDivisor); for (;;) { - if (bufRem.Low64 == 0) + if ((rgulRem[0] | rgulRem[1]) == 0) { if (iScale < 0) { @@ -2220,30 +2134,39 @@ ReturnZero: // Remainder is non-zero. Scale up quotient and remainder by // powers of 10 so we can compute more significant bits. // - if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); + if (iCurScale == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // - ulong tmp = bufRem.Low64; - if ((long)tmp < 0 || (tmp <<= 1) > divisor || - (tmp == divisor && (bufQuo.U0 & 1) != 0)) - goto RoundUp; + sdlTmp.Low32 = rgulRem[0]; + sdlTmp.High32 = rgulRem[1]; + if (sdlTmp.High32 >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || + (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1) != 0)) + RoundUp(rgulQuo, ref iScale); break; } + if (iCurScale < 0) + throw new OverflowException(SR.Overflow_Decimal); + HaveScale64: ulPwr = s_powers10[iCurScale]; iScale += iCurScale; - if (IncreaseScale(ref bufQuo, ulPwr) != 0) - goto ThrowOverflow; + if (IncreaseScale(rgulQuo, ulPwr) != 0) + throw new OverflowException(SR.Overflow_Decimal); - IncreaseScale64(ref *(Buf12*)&bufRem, ulPwr); - ulTmp = Div96By64(ref *(Buf12*)&bufRem, divisor); - if (!Add32To96(ref bufQuo, ulTmp)) + rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 + IncreaseScale(rgulRem, ulPwr); + ulTmp = Div96By64(rgulRem, sdlDivisor); + if (!Add32To96(rgulQuo, ulTmp)) { - iScale = OverflowUnscale(ref bufQuo, iScale, bufRem.Low64 != 0); + if (iScale == 0) + throw new OverflowException(SR.Overflow_Decimal); + iScale--; + OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0)); break; } } // for (;;) @@ -2254,18 +2177,23 @@ ReturnZero: // // Start by finishing the shift left by iCurScale. // - uint tmp = (uint)(bufDivisor.High64 >> (31 - iCurScale) >> 1); - bufDivisor.Low64 = divisor; - bufDivisor.U2 = tmp; + sdlTmp.Low32 = rgulDivisor[1]; + sdlTmp.High32 = rgulDivisor[2]; + sdlTmp.int64 <<= iCurScale; + rgulDivisor[0] = sdlDivisor.Low32; + rgulDivisor[1] = sdlDivisor.High32; + rgulDivisor[2] = sdlTmp.High32; // The remainder (currently 96 bits spread over 4 ULONGs) // will be < divisor. // - bufQuo.Low64 = Div128By96(ref bufRem, ref bufDivisor); + rgulQuo[2] = 0; + rgulQuo[1] = 0; + rgulQuo[0] = Div128By96(rgulRem, rgulDivisor); for (;;) { - if ((bufRem.Low64 | bufRem.U2) == 0) + if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) { if (iScale < 0) { @@ -2281,48 +2209,56 @@ ReturnZero: // Remainder is non-zero. Scale up quotient and remainder by // powers of 10 so we can compute more significant bits. // - if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); + if (iCurScale == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // - if ((int)bufRem.U2 < 0) + if (rgulRem[2] >= 0x80000000) { - goto RoundUp; + RoundUp(rgulQuo, ref iScale); + break; } - ulTmp = bufRem.U1 >> 31; - bufRem.Low64 <<= 1; - bufRem.U2 = (bufRem.U2 << 1) + ulTmp; - - if (bufRem.U2 > bufDivisor.U2 || bufRem.U2 == bufDivisor.U2 && - (bufRem.Low64 > bufDivisor.Low64 || bufRem.Low64 == bufDivisor.Low64 && - (bufQuo.U0 & 1) != 0)) - goto RoundUp; + ulTmp = (rgulRem[0] > 0x80000000) ? 1u : 0u; + ulTmp1 = (rgulRem[1] > 0x80000000) ? 1u : 0u; + rgulRem[0] <<= 1; + rgulRem[1] = (rgulRem[1] << 1) + ulTmp; + rgulRem[2] = (rgulRem[2] << 1) + ulTmp1; + + if (rgulRem[2] > rgulDivisor[2] || rgulRem[2] == rgulDivisor[2] && + (rgulRem[1] > rgulDivisor[1] || rgulRem[1] == rgulDivisor[1] && + (rgulRem[0] > rgulDivisor[0] || rgulRem[0] == rgulDivisor[0] && + (rgulQuo[0] & 1) != 0))) + RoundUp(rgulQuo, ref iScale); break; } + if (iCurScale < 0) + throw new OverflowException(SR.Overflow_Decimal); + HaveScale96: ulPwr = s_powers10[iCurScale]; iScale += iCurScale; - if (IncreaseScale(ref bufQuo, ulPwr) != 0) - goto ThrowOverflow; + if (IncreaseScale(rgulQuo, ulPwr) != 0) + throw new OverflowException(SR.Overflow_Decimal); - bufRem.U3 = IncreaseScale(ref *(Buf12*)&bufRem, ulPwr); - ulTmp = Div128By96(ref bufRem, ref bufDivisor); - if (!Add32To96(ref bufQuo, ulTmp)) + rgulRem[3] = IncreaseScale(rgulRem, ulPwr); + ulTmp = Div128By96(rgulRem, rgulDivisor); + if (!Add32To96(rgulQuo, ulTmp)) { - iScale = OverflowUnscale(ref bufQuo, iScale, (bufRem.Low64 | bufRem.High64) != 0); + if (iScale == 0) + throw new OverflowException(SR.Overflow_Decimal); + iScale--; + OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0)); break; } } // for (;;) } } -Unscale: - ulong low64 = bufQuo.Low64; - uint high = bufQuo.U2; // We need to unscale if and only if we have a non-zero remainder if (fUnscale) { @@ -2336,79 +2272,74 @@ Unscale: // we can extract. We use this as a quick test on whether to try a // given power. // - while (((uint)low64 & 0xFF) == 0 && iScale >= 8) + while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) { - if (Div96ByConst100000000(ref bufQuo) == 0) + rgulQuoSave[0] = rgulQuo[0]; + rgulQuoSave[1] = rgulQuo[1]; + rgulQuoSave[2] = rgulQuo[2]; + + if (Div96By32(rgulQuoSave, 100000000) == 0) { - low64 = bufQuo.Low64; - high = bufQuo.U2; + rgulQuo[0] = rgulQuoSave[0]; + rgulQuo[1] = rgulQuoSave[1]; + rgulQuo[2] = rgulQuoSave[2]; iScale -= 8; } else - { - bufQuo.Low64 = low64; - bufQuo.U2 = high; break; - } } - if (((uint)low64 & 0xF) == 0 && iScale >= 4) + if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) { - if (Div96ByConst10000(ref bufQuo) == 0) + rgulQuoSave[0] = rgulQuo[0]; + rgulQuoSave[1] = rgulQuo[1]; + rgulQuoSave[2] = rgulQuo[2]; + + if (Div96By32(rgulQuoSave, 10000) == 0) { - low64 = bufQuo.Low64; - high = bufQuo.U2; + rgulQuo[0] = rgulQuoSave[0]; + rgulQuo[1] = rgulQuoSave[1]; + rgulQuo[2] = rgulQuoSave[2]; iScale -= 4; } - else - { - bufQuo.Low64 = low64; - bufQuo.U2 = high; - } } - if (((uint)low64 & 3) == 0 && iScale >= 2) + if ((rgulQuo[0] & 3) == 0 && iScale >= 2) { - if (Div96ByConst100(ref bufQuo) == 0) + rgulQuoSave[0] = rgulQuo[0]; + rgulQuoSave[1] = rgulQuo[1]; + rgulQuoSave[2] = rgulQuo[2]; + + if (Div96By32(rgulQuoSave, 100) == 0) { - low64 = bufQuo.Low64; - high = bufQuo.U2; + rgulQuo[0] = rgulQuoSave[0]; + rgulQuo[1] = rgulQuoSave[1]; + rgulQuo[2] = rgulQuoSave[2]; iScale -= 2; } - else - { - bufQuo.Low64 = low64; - bufQuo.U2 = high; - } } - if (((uint)low64 & 1) == 0 && iScale >= 1 && Div96ByConst10(ref bufQuo) == 0) + if ((rgulQuo[0] & 1) == 0 && iScale >= 1) { - low64 = bufQuo.Low64; - high = bufQuo.U2; - iScale -= 1; - } - } + rgulQuoSave[0] = rgulQuo[0]; + rgulQuoSave[1] = rgulQuo[1]; + rgulQuoSave[2] = rgulQuo[2]; - d1.uflags = ((d1.uflags ^ d2.uflags) & SignMask) | ((uint)iScale << ScaleShift); - d1.High = high; - d1.Low64 = low64; - return; - -RoundUp: - { - if (++bufQuo.Low64 == 0) - { - if (++bufQuo.U2 == 0) + if (Div96By32(rgulQuoSave, 10) == 0) { - iScale = OverflowUnscale(ref bufQuo, iScale, true); + rgulQuo[0] = rgulQuoSave[0]; + rgulQuo[1] = rgulQuoSave[1]; + rgulQuo[2] = rgulQuoSave[2]; + iScale -= 1; } } - goto Unscale; } -ThrowOverflow: - throw new OverflowException(SR.Overflow_Decimal); + d1.IsNegative = d1.IsNegative ^ d2.IsNegative; + d1.High = rgulQuo[2]; + d1.Mid = rgulQuo[1]; + d1.Low = rgulQuo[0]; + d1.Scale = iScale; } //********************************************************************** @@ -2444,10 +2375,9 @@ ThrowOverflow: //********************************************************************** // VarDecRound - Decimal Round //********************************************************************** - internal static unsafe void VarDecRound(ref Decimal input, int decimals, ref Decimal result) + internal static void VarDecRound(ref Decimal input, int decimals, ref Decimal result) { - Buf12 bufNum; - _ = &bufNum; // workaround for CS0165 + uint[] tmpNum = new uint[3]; uint remainder; uint sticky; uint power; @@ -2458,9 +2388,9 @@ ThrowOverflow: scale = input.Scale - decimals; if (scale > 0) { - bufNum.U0 = input.ulo; - bufNum.U1 = input.umid; - bufNum.U2 = input.uhi; + tmpNum[0] = input.ulo; + tmpNum[1] = input.umid; + tmpNum[2] = input.uhi; result.IsNegative = input.IsNegative; remainder = sticky = 0; @@ -2472,23 +2402,23 @@ ThrowOverflow: else power = s_powers10[scale]; - remainder = Div96By32(ref bufNum, power); + remainder = Div96By32(tmpNum, power); scale -= MaxInt32Scale; } while (scale > 0); // Now round. ulRem has last remainder, ulSticky has sticky bits. // To do IEEE rounding, we add LSB of result to sticky bits so // either causes round up if remainder * 2 == last divisor. - sticky |= bufNum.U0 & 1; + sticky |= tmpNum[0] & 1; remainder = (remainder << 1) + (uint)(sticky != 0 ? 1 : 0); if (power < remainder - && ++bufNum.U0 == 0 - && ++bufNum.U1 == 0) - ++bufNum.U2; + && ++tmpNum[0] == 0 + && ++tmpNum[1] == 0) + ++tmpNum[2]; - result.ulo = bufNum.U0; - result.umid = bufNum.U1; - result.uhi = bufNum.U2; + result.ulo = tmpNum[0]; + result.umid = tmpNum[1]; + result.uhi = tmpNum[2]; result.Scale = decimals; return; } @@ -2721,174 +2651,46 @@ ThrowOverflow: } } - struct PowerOvfl - { - public readonly uint Hi; - public readonly ulong MidLo; - - public PowerOvfl(uint hi, uint mid, uint lo) - { - Hi = hi; - MidLo = ((ulong)mid << 32) + lo; - } - } - - static readonly PowerOvfl[] PowerOvflValues = new[] - { - // This is a table of the largest values that can be in the upper two - // ULONGs of a 96-bit number that will not overflow when multiplied - // by a given power. For the upper word, this is a table of - // 2^32 / 10^n for 1 <= n <= 8. For the lower word, this is the - // remaining fraction part * 2^32. 2^32 = 4294967296. - // - new PowerOvfl(429496729, 2576980377, 2576980377), // 10^1 remainder 0.6 - new PowerOvfl(42949672, 4123168604, 687194767), // 10^2 remainder 0.16 - new PowerOvfl(4294967, 1271310319, 2645699854), // 10^3 remainder 0.616 - new PowerOvfl(429496, 3133608139, 694066715), // 10^4 remainder 0.1616 - new PowerOvfl(42949, 2890341191, 2216890319), // 10^5 remainder 0.51616 - new PowerOvfl(4294, 4154504685, 2369172679), // 10^6 remainder 0.551616 - new PowerOvfl(429, 2133437386, 4102387834), // 10^7 remainder 0.9551616 - new PowerOvfl(42, 4078814305, 410238783), // 10^8 remainder 0.09991616 - }; - - [StructLayout(LayoutKind.Explicit)] - private struct Buf12 - { - [FieldOffset(0 * 4)] - public uint U0; - [FieldOffset(1 * 4)] - public uint U1; - [FieldOffset(2 * 4)] - public uint U2; - - [FieldOffset(0)] - private ulong ulo64LE; - [FieldOffset(4)] - private ulong uhigh64LE; - - public ulong Low64 - { -#if BIGENDIAN - get => ((ulong)U1 << 32) | U0; - set { U1 = (uint)(value >> 32); U0 = (uint)value; } -#else - get => ulo64LE; - set => ulo64LE = value; -#endif - } - - /// <summary> - /// U1-U2 combined (overlaps with Low64) - /// </summary> - public ulong High64 - { -#if BIGENDIAN - get => ((ulong)U2 << 32) | U1; - set { U2 = (uint)(value >> 32); U1 = (uint)value; } -#else - get => uhigh64LE; - set => uhigh64LE = value; -#endif - } - } - - [StructLayout(LayoutKind.Explicit)] - private struct Buf16 + private static class PowerOvfl { - [FieldOffset(0 * 4)] - public uint U0; - [FieldOffset(1 * 4)] - public uint U1; - [FieldOffset(2 * 4)] - public uint U2; - [FieldOffset(3 * 4)] - public uint U3; - - [FieldOffset(0 * 8)] - private ulong ulo64LE; - [FieldOffset(1 * 8)] - private ulong uhigh64LE; - - public ulong Low64 - { -#if BIGENDIAN - get => ((ulong)U1 << 32) | U0; - set { U1 = (uint)(value >> 32); U0 = (uint)value; } -#else - get => ulo64LE; - set => ulo64LE = value; -#endif - } - - public ulong High64 + private static uint[] s_powerOvfl = { -#if BIGENDIAN - get => ((ulong)U3 << 32) | U2; - set { U3 = (uint)(value >> 32); U2 = (uint)value; } -#else - get => uhigh64LE; - set => uhigh64LE = value; -#endif - } - } + // This is a table of the largest values that can be in the upper two + // ULONGs of a 96-bit number that will not overflow when multiplied + // by a given power. For the upper word, this is a table of + // 2^32 / 10^n for 1 <= n <= 9. For the lower word, this is the + // remaining fraction part * 2^32. 2^32 = 4294967296. + // + // Table logically consists of three components for each entry (high, + // mid and low) but we declare it as a flat array of uints since that's + // all C# will support. We hide this via the accessor methods on this + // class. + // + 429496729, 2576980377, 2576980377, // 10^1 remainder 0.6 + 42949672, 4123168604, 687194767, // 10^2 remainder 0.16 + 4294967, 1271310319, 2645699854, // 10^3 remainder 0.616 + 429496, 3133608139, 694066715, // 10^4 remainder 0.1616 + 42949, 2890341191, 2216890319, // 10^5 remainder 0.51616 + 4294, 4154504685, 2369172679, // 10^6 remainder 0.551616 + 429, 2133437386, 4102387834, // 10^7 remainder 0.9551616 + 42, 4078814305, 410238783, // 10^8 remainder 0.09991616 + 4, 1266874889, 3047500985, // 10^9 remainder 0.709551616 + }; - [StructLayout(LayoutKind.Explicit)] - private struct Buf24 - { - [FieldOffset(0 * 4)] - public uint U0; - [FieldOffset(1 * 4)] - public uint U1; - [FieldOffset(2 * 4)] - public uint U2; - [FieldOffset(3 * 4)] - public uint U3; - [FieldOffset(4 * 4)] - public uint U4; - [FieldOffset(5 * 4)] - public uint U5; - - [FieldOffset(0 * 8)] - private ulong ulo64LE; - [FieldOffset(1 * 8)] - private ulong umid64LE; - [FieldOffset(2 * 8)] - private ulong uhigh64LE; - - public ulong Low64 + public static uint Hi(int index) { -#if BIGENDIAN - get => ((ulong)U1 << 32) | U0; - set { U1 = (uint)(value >> 32); U0 = (uint)value; } -#else - get => ulo64LE; - set => ulo64LE = value; -#endif + return s_powerOvfl[index * 3]; } - public ulong Mid64 + public static uint Mid(int index) { -#if BIGENDIAN - get => ((ulong)U3 << 32) | U2; - set { U3 = (uint)(value >> 32); U2 = (uint)value; } -#else - get => umid64LE; - set => umid64LE = value; -#endif + return s_powerOvfl[(index * 3) + 1]; } - public ulong High64 + public static uint Lo(int index) { -#if BIGENDIAN - get => ((ulong)U5 << 32) | U4; - set { U5 = (uint)(value >> 32); U4 = (uint)value; } -#else - get => uhigh64LE; - set => uhigh64LE = value; -#endif + return s_powerOvfl[(index * 3) + 2]; } - - public int Length => 6; } } } diff --git a/src/System.Private.CoreLib/src/System/Decimal.cs b/src/System.Private.CoreLib/src/System/Decimal.cs index 2fc98da8c..8d89068f7 100644 --- a/src/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/System.Private.CoreLib/src/System/Decimal.cs @@ -123,11 +123,13 @@ namespace System [FieldOffset(12), NonSerialized] private uint umid; - /// <summary> - /// The low and mid fields combined in little-endian order - /// </summary> - [FieldOffset(8), NonSerialized] - private ulong ulomidLE; + // Constructs a zero Decimal. + //public Decimal() { + // lo = 0; + // mid = 0; + // hi = 0; + // flags = 0; + //} // Constructs a Decimal from an integer value. // @@ -177,7 +179,8 @@ namespace System uflags = SignMask; value_copy = -value_copy; } - Low64 = (ulong)value; + ulo = (uint)value_copy; + umid = (uint)(value_copy >> 32); uhi = 0; } @@ -187,7 +190,8 @@ namespace System public Decimal(ulong value) { uflags = 0; - Low64 = value; + ulo = (uint)value; + umid = (uint)(value >> 32); uhi = 0; } @@ -276,6 +280,10 @@ namespace System // public Decimal(int[] bits) { + lo = 0; + mid = 0; + hi = 0; + flags = 0; SetBits(bits); } @@ -644,8 +652,9 @@ namespace System // public static Decimal Multiply(Decimal d1, Decimal d2) { - DecCalc.VarDecMul(ref d1, ref d2); - return d1; + Decimal decRes; + DecCalc.VarDecMul(ref d1, ref d2, out decRes); + return decRes; } // Returns the negated value of the given Decimal. If d is non-zero, diff --git a/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs index 50e93841f..55a6ca4bf 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using System.Collections.Generic; namespace System.Globalization @@ -14,6 +14,8 @@ namespace System.Globalization { private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) { + Debug.Assert(!GlobalizationMode.Invariant); + bool ret = true; uint useOverrides = this.bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE; @@ -108,6 +110,11 @@ namespace System.Globalization // Get native two digit year max internal static int GetTwoDigitYearMax(CalendarId calendarId) { + if (GlobalizationMode.Invariant) + { + return Invariant.iTwoDigitYearMax; + } + int twoDigitYearMax = -1; if (!CallGetCalendarInfoEx(null, calendarId, (uint)CAL_ITWODIGITYEARMAX, out twoDigitYearMax)) @@ -121,6 +128,8 @@ namespace System.Globalization // Call native side to figure out which calendars are allowed internal static int GetCalendars(String localeName, bool useUserOverride, CalendarId[] calendars) { + Debug.Assert(!GlobalizationMode.Invariant); + EnumCalendarsData data = new EnumCalendarsData(); data.userOverride = 0; data.calendars = new LowLevelList<int>(); @@ -154,6 +163,8 @@ namespace System.Globalization private static bool SystemSupportsTaiwaneseCalendar() { + Debug.Assert(!GlobalizationMode.Invariant); + string data; // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out data); @@ -442,6 +453,8 @@ namespace System.Globalization private static unsafe String GetUserDefaultLocaleName() { + Debug.Assert(!GlobalizationMode.Invariant); + const int LOCALE_NAME_MAX_LENGTH = 85; const uint LOCALE_SNAME = 0x0000005c; const string LOCALE_NAME_USER_DEFAULT = null; diff --git a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index 31f5a8b63..04c9019df 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -71,6 +71,10 @@ namespace System.Globalization [OptionalField(VersionAdded = 3)] private SortVersion m_SortVersion; // Do not rename (binary serialization) + // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant + [NonSerialized] + private readonly bool _invariantMode = GlobalizationMode.Invariant; + private int culture; // Do not rename (binary serialization). The fields sole purpose is to support Desktop serialization. internal CompareInfo(CultureInfo culture) diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs index 28ebe7239..4f67cc6a8 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs @@ -630,13 +630,17 @@ namespace System.Globalization return null; } - private int LocaleNameToLCID(string cultureName) + private static int LocaleNameToLCID(string cultureName) { + Debug.Assert(!GlobalizationMode.Invariant); + return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); } private static unsafe string LCIDToLocaleName(int culture) { + Debug.Assert(!GlobalizationMode.Invariant); + char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); @@ -685,6 +689,8 @@ namespace System.Globalization private static CultureInfo[] EnumCultures(CultureTypes types) { + Debug.Assert(!GlobalizationMode.Invariant); + uint flags = 0; #pragma warning disable 618 diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs index e9fe86ace..5a06bccd4 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs @@ -13,6 +13,9 @@ namespace System.Globalization internal static CultureInfo GetUserDefaultCulture() { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + CultureInfo cultureInfo = null; string localeName; if (CultureData.GetDefaultLocaleName(out localeName)) diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs index b395c1a7f..f5a6992e6 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs @@ -31,6 +31,9 @@ namespace System.Globalization internal static CultureInfo GetUserDefaultCulture() { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + const uint LOCALE_SNAME = 0x0000005c; const string LOCALE_NAME_USER_DEFAULT = null; const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale"; diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs new file mode 100644 index 000000000..e4ab832d5 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs @@ -0,0 +1,26 @@ +// 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. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static bool GetGlobalizationInvariantMode() + { + // CORERT-TODO: Enable System.Globalization.Invariant switch + // bool invariantEnabled = CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch); + bool invariantEnabled = false; + if (!invariantEnabled) + { + if (Interop.GlobalizationInterop.LoadICU() == 0) + { + string message = "Couldn't find a valid ICU package installed on the system. " + + "Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support."; + Environment.FailFast(message); + } + } + return invariantEnabled; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs new file mode 100644 index 000000000..7c6bf0161 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static bool GetGlobalizationInvariantMode() + { + // CORERT-TODO: Enable System.Globalization.Invariant switch + // return CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch); + return false; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index ea56795c9..266774d91 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -4,8 +4,9 @@ namespace System.Globalization { - internal static class GlobalizationMode + internal static partial class GlobalizationMode { - internal static bool Invariant { get; } = false; + private const string c_InvariantModeConfigSwitch = "System.Globalization.Invariant"; + internal static bool Invariant { get; } = GetGlobalizationInvariantMode(); } } |