summaryrefslogtreecommitdiff
path: root/src/classlibnative
diff options
context:
space:
mode:
Diffstat (limited to 'src/classlibnative')
-rw-r--r--src/classlibnative/.gitmirror1
-rw-r--r--src/classlibnative/CMakeLists.txt12
-rw-r--r--src/classlibnative/Dirs.proj29
-rw-r--r--src/classlibnative/bcltype/.gitmirror1
-rw-r--r--src/classlibnative/bcltype/BCLType.nativeproj47
-rw-r--r--src/classlibnative/bcltype/CMakeLists.txt32
-rw-r--r--src/classlibnative/bcltype/arrayhelpers.cpp427
-rw-r--r--src/classlibnative/bcltype/arrayhelpers.h348
-rw-r--r--src/classlibnative/bcltype/arraynative.cpp1562
-rw-r--r--src/classlibnative/bcltype/arraynative.h81
-rw-r--r--src/classlibnative/bcltype/console.cpp152
-rw-r--r--src/classlibnative/bcltype/console.h45
-rw-r--r--src/classlibnative/bcltype/currency.cpp42
-rw-r--r--src/classlibnative/bcltype/currency.h24
-rw-r--r--src/classlibnative/bcltype/decimal.cpp2588
-rw-r--r--src/classlibnative/bcltype/decimal.h54
-rw-r--r--src/classlibnative/bcltype/number.cpp3005
-rw-r--r--src/classlibnative/bcltype/number.h61
-rw-r--r--src/classlibnative/bcltype/oavariant.cpp430
-rw-r--r--src/classlibnative/bcltype/oavariant.h46
-rw-r--r--src/classlibnative/bcltype/objectnative.cpp371
-rw-r--r--src/classlibnative/bcltype/objectnative.h43
-rw-r--r--src/classlibnative/bcltype/stringbuffer.cpp48
-rw-r--r--src/classlibnative/bcltype/stringbuffer.h38
-rw-r--r--src/classlibnative/bcltype/stringnative.cpp736
-rw-r--r--src/classlibnative/bcltype/stringnative.h106
-rw-r--r--src/classlibnative/bcltype/system.cpp888
-rw-r--r--src/classlibnative/bcltype/system.h158
-rw-r--r--src/classlibnative/bcltype/varargsnative.cpp638
-rw-r--r--src/classlibnative/bcltype/varargsnative.h33
-rw-r--r--src/classlibnative/bcltype/variant.cpp361
-rw-r--r--src/classlibnative/bcltype/variant.h55
-rw-r--r--src/classlibnative/bcltype/windowsruntimebufferhelper.cpp44
-rw-r--r--src/classlibnative/bcltype/windowsruntimebufferhelper.h27
-rw-r--r--src/classlibnative/float/.gitmirror1
-rw-r--r--src/classlibnative/float/CMakeLists.txt17
-rw-r--r--src/classlibnative/float/Float.nativeproj36
-rw-r--r--src/classlibnative/float/floatdouble.cpp280
-rw-r--r--src/classlibnative/float/floatsingle.cpp54
-rw-r--r--src/classlibnative/inc/.gitmirror1
-rw-r--r--src/classlibnative/inc/calendardata.h165
-rw-r--r--src/classlibnative/inc/floatdouble.h35
-rw-r--r--src/classlibnative/inc/floatsingle.h16
-rw-r--r--src/classlibnative/inc/nls.h34
-rw-r--r--src/classlibnative/inc/nlsinfo.h249
-rw-r--r--src/classlibnative/inc/nlstable.h121
-rw-r--r--src/classlibnative/nls/.gitmirror1
-rw-r--r--src/classlibnative/nls/CMakeLists.txt8
-rw-r--r--src/classlibnative/nls/NLS.nativeproj37
-rw-r--r--src/classlibnative/nls/calendardata.cpp985
-rw-r--r--src/classlibnative/nls/encodingdata.cpp682
-rw-r--r--src/classlibnative/nls/nlsinfo.cpp3476
-rw-r--r--src/classlibnative/nls/nlstable.cpp259
53 files changed, 18990 insertions, 0 deletions
diff --git a/src/classlibnative/.gitmirror b/src/classlibnative/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/classlibnative/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/classlibnative/CMakeLists.txt b/src/classlibnative/CMakeLists.txt
new file mode 100644
index 0000000000..1c8d82566f
--- /dev/null
+++ b/src/classlibnative/CMakeLists.txt
@@ -0,0 +1,12 @@
+include_directories(BEFORE "../vm")
+include_directories("../inc")
+include_directories(BEFORE "../vm/${ARCH_SOURCES_DIR}")
+include_directories("../debug/inc")
+include_directories("../debug/inc/dump")
+
+add_subdirectory(bcltype)
+add_subdirectory(float)
+
+if(WIN32)
+ add_subdirectory(nls)
+endif(WIN32)
diff --git a/src/classlibnative/Dirs.proj b/src/classlibnative/Dirs.proj
new file mode 100644
index 0000000000..0007a40314
--- /dev/null
+++ b/src/classlibnative/Dirs.proj
@@ -0,0 +1,29 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1 DESKTOP -->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' != 'true'">
+ <ProjectFile Include="bcltype\bcltype.nativeproj" />
+ <ProjectFile Include="cryptography\cryptography.nativeproj" />
+ <ProjectFile Include="float\float.nativeproj" />
+ <ProjectFile Include="nls\nls.nativeproj" />
+ <ProjectFile Include="remoting\remoting.nativeproj" />
+ </ItemGroup>
+
+ <!--The following projects will build during PHASE 1 CORE -->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' == 'true'">
+ <ProjectFile Include="bcltype\bcltype.nativeproj" />
+ <ProjectFile Include="cryptography\cryptography.nativeproj" />
+ <ProjectFile Include="float\float.nativeproj" />
+ <ProjectFile Include="nls\nls.nativeproj" Condition="'$(FeatureCoreFxGlobalization)' != 'true'"/>
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/classlibnative/bcltype/.gitmirror b/src/classlibnative/bcltype/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/classlibnative/bcltype/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/classlibnative/bcltype/BCLType.nativeproj b/src/classlibnative/bcltype/BCLType.nativeproj
new file mode 100644
index 0000000000..719a420313
--- /dev/null
+++ b/src/classlibnative/bcltype/BCLType.nativeproj
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(ClrBase)\src\debug\SetDebugTargetLocal.props" />
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <UserIncludes>..\..\inc;$(Clrbase)\src\vm;$(Clrbase)\src\vm\$(TargetCpu);..\..\debug\inc\$(TargetCpu);..\..\debug\inc\dump;$(UserIncludes);$(VCToolsIncPath)</UserIncludes>
+ <OutputName>bcltype</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines>
+ <ClAdditionalOptions Condition="'$(PerfCountersSupportedBuild)' == 'true'">$(ClAdditionalOptions) -DENABLE_PERF_COUNTERS</ClAdditionalOptions>
+ <ClWarningLevel>4</ClWarningLevel>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="ArrayNative.cpp" />
+ <CppCompile Include="ArrayHelpers.cpp" />
+ <CppCompile Include="Console.cpp" />
+ <CppCompile Include="Currency.cpp" />
+ <CppCompile Include="Decimal.cpp" />
+ <CppCompile Include="WindowsRuntimeBufferHelper.cpp" />
+ <CppCompile Include="Number.cpp" />
+ <CppCompile Include="OAVariant.cpp" />
+ <CppCompile Include="ObjectNative.cpp" />
+ <CppCompile Include="StringNative.cpp" />
+ <CppCompile Include="StringBuffer.cpp" />
+ <CppCompile Include="System.cpp" />
+ <CppCompile Include="VarArgsNative.cpp" />
+ <CppCompile Include="Variant.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt
new file mode 100644
index 0000000000..4df909ae35
--- /dev/null
+++ b/src/classlibnative/bcltype/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+if(PerfCountersSupportedBuild)
+ add_definitions(-DENABLE_PERF_COUNTERS)
+endif(PerfCountersSupportedBuild)
+
+set(BCLTYPE_SOURCES
+ arraynative.cpp
+ arrayhelpers.cpp
+ console.cpp
+ currency.cpp
+ decimal.cpp
+ windowsruntimebufferhelper.cpp
+ number.cpp
+ oavariant.cpp
+ objectnative.cpp
+ stringnative.cpp
+ stringbuffer.cpp
+ system.cpp
+ varargsnative.cpp
+ variant.cpp
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_library_clr(bcltype
+ STATIC
+ ${BCLTYPE_SOURCES}
+)
+
diff --git a/src/classlibnative/bcltype/arrayhelpers.cpp b/src/classlibnative/bcltype/arrayhelpers.cpp
new file mode 100644
index 0000000000..45e86d2b08
--- /dev/null
+++ b/src/classlibnative/bcltype/arrayhelpers.cpp
@@ -0,0 +1,427 @@
+// 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.
+//
+// File: ArrayHelpers.cpp
+//
+
+//
+
+#include "common.h"
+
+#include <object.h>
+#include "ceeload.h"
+
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "classnames.h"
+#include "arrayhelpers.h"
+#include <memory.h>
+
+INT32 ArrayHelper::IndexOfUINT8( UINT8* array, UINT32 index, UINT32 count, UINT8 value) {
+ LIMITED_METHOD_CONTRACT;
+ UINT8 * pvalue = (UINT8 *)memchr(array + index, value, count);
+ if ( NULL == pvalue ) {
+ return -1;
+ }
+ else {
+ return static_cast<INT32>(pvalue - array);
+ }
+}
+
+
+// A fast IndexOf method for arrays of primitive types. Returns TRUE or FALSE
+// if it succeeds, and stores result in retVal.
+FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+ _ASSERTE(array != NULL);
+
+ // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
+ // non-zero lower bounds. VB might care. </TODO>
+ if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
+ FC_RETURN_BOOL(FALSE);
+
+ _ASSERTE(retVal != NULL);
+ _ASSERTE(index <= array->GetNumComponents());
+ _ASSERTE(count <= array->GetNumComponents());
+ _ASSERTE(array->GetNumComponents() >= index + count);
+ *retVal = 0xdeadbeef; // Initialize the return value.
+ // value can be NULL, but of course, will not be in primitive arrays.
+
+ TypeHandle arrayTH = array->GetArrayElementTypeHandle();
+ const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
+ FC_RETURN_BOOL(FALSE);
+ // Handle special case of looking for a NULL object in a primitive array.
+ if (value == NULL) {
+ *retVal = -1;
+ FC_RETURN_BOOL(TRUE);
+ }
+ TypeHandle valueTH = value->GetTypeHandle();
+ if (arrayTH != valueTH)
+ FC_RETURN_BOOL(FALSE);
+
+
+ switch(arrayElType) {
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ *retVal = IndexOfUINT8((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ *retVal = ArrayHelpers<U2>::IndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ IN_WIN32(case ELEMENT_TYPE_I:)
+ IN_WIN32(case ELEMENT_TYPE_U:)
+ *retVal = ArrayHelpers<U4>::IndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ IN_WIN64(case ELEMENT_TYPE_I:)
+ IN_WIN64(case ELEMENT_TYPE_U:)
+ *retVal = ArrayHelpers<U8>::IndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
+ break;
+
+ default:
+ _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZIndexOf");
+ FC_RETURN_BOOL(FALSE);
+ }
+ FC_RETURN_BOOL(TRUE);
+FCIMPLEND
+
+// A fast LastIndexOf method for arrays of primitive types. Returns TRUE or FALSE
+// if it succeeds, and stores result in retVal.
+FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZLastIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+ _ASSERTE(array != NULL);
+
+ // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
+ // non-zero lower bounds. VB might care. </TODO>
+ if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
+ FC_RETURN_BOOL(FALSE);
+
+ _ASSERTE(retVal != NULL);
+ *retVal = 0xdeadbeef; // Initialize the return value.
+ // value can be NULL, but of course, will not be in primitive arrays.
+
+ TypeHandle arrayTH = array->GetArrayElementTypeHandle();
+ const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
+ FC_RETURN_BOOL(FALSE);
+ // Handle special case of looking for a NULL object in a primitive array.
+ // Also handle case where the array is of 0 length.
+ if (value == NULL || array->GetNumComponents() == 0) {
+ *retVal = -1;
+ FC_RETURN_BOOL(TRUE);
+ }
+
+ _ASSERTE(index < array->GetNumComponents());
+ _ASSERTE(count <= array->GetNumComponents());
+ _ASSERTE(index + 1 >= count);
+
+ TypeHandle valueTH = value->GetTypeHandle();
+ if (arrayTH != valueTH)
+ FC_RETURN_BOOL(FALSE);
+
+
+ switch(arrayElType) {
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ *retVal = ArrayHelpers<U1>::LastIndexOf((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ *retVal = ArrayHelpers<U2>::LastIndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ IN_WIN32(case ELEMENT_TYPE_I:)
+ IN_WIN32(case ELEMENT_TYPE_U:)
+ *retVal = ArrayHelpers<U4>::LastIndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ IN_WIN64(case ELEMENT_TYPE_I:)
+ IN_WIN64(case ELEMENT_TYPE_U:)
+ *retVal = ArrayHelpers<U8>::LastIndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
+ break;
+
+ default:
+ _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZLastIndexOf");
+ FC_RETURN_BOOL(FALSE);
+ }
+ FC_RETURN_BOOL(TRUE);
+}
+FCIMPLEND
+
+
+FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+ _ASSERTE(array != NULL);
+
+ // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
+ // non-zero lower bounds. VB might care. </TODO>
+ if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
+ FC_RETURN_BOOL(FALSE);
+
+ _ASSERTE(retVal != NULL);
+ _ASSERTE(index <= array->GetNumComponents());
+ _ASSERTE(count <= array->GetNumComponents());
+ _ASSERTE(array->GetNumComponents() >= index + count);
+ *retVal = 0xdeadbeef; // Initialize the return value.
+ // value can be NULL, but of course, will not be in primitive arrays.
+ TypeHandle arrayTH = array->GetArrayElementTypeHandle();
+ const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
+ FC_RETURN_BOOL(FALSE);
+ // Handle special case of looking for a NULL object in a primitive array.
+ if (value == NULL) {
+ *retVal = -1;
+ FC_RETURN_BOOL(TRUE);
+ }
+
+ TypeHandle valueTH = value->GetTypeHandle();
+ if (arrayTH != valueTH)
+ FC_RETURN_BOOL(FALSE);
+
+ switch(arrayElType) {
+ case ELEMENT_TYPE_I1:
+ *retVal = ArrayHelpers<I1>::BinarySearchBitwiseEquals((I1*) array->GetDataPtr(), index, count, *(I1*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ *retVal = ArrayHelpers<U1>::BinarySearchBitwiseEquals((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I2:
+ *retVal = ArrayHelpers<I2>::BinarySearchBitwiseEquals((I2*) array->GetDataPtr(), index, count, *(I2*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ *retVal = ArrayHelpers<U2>::BinarySearchBitwiseEquals((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I4:
+ *retVal = ArrayHelpers<I4>::BinarySearchBitwiseEquals((I4*) array->GetDataPtr(), index, count, *(I4*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_U4:
+ *retVal = ArrayHelpers<U4>::BinarySearchBitwiseEquals((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_R4:
+ *retVal = ArrayHelpers<R4>::BinarySearchBitwiseEquals((R4*) array->GetDataPtr(), index, count, *(R4*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I8:
+ *retVal = ArrayHelpers<I8>::BinarySearchBitwiseEquals((I8*) array->GetDataPtr(), index, count, *(I8*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_U8:
+ *retVal = ArrayHelpers<U8>::BinarySearchBitwiseEquals((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *retVal = ArrayHelpers<R8>::BinarySearchBitwiseEquals((R8*) array->GetDataPtr(), index, count, *(R8*)value->UnBox());
+ break;
+
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ // In V1.0, IntPtr & UIntPtr are not fully supported types. They do
+ // not implement IComparable, so searching & sorting for them should
+ // fail. In V1.1 or V2.0, this should change.
+ FC_RETURN_BOOL(FALSE);
+
+ default:
+ _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZBinarySearch");
+ FC_RETURN_BOOL(FALSE);
+ }
+ FC_RETURN_BOOL(TRUE);
+FCIMPLEND
+
+FCIMPL4(FC_BOOL_RET, ArrayHelper::TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right)
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(keys);
+ VALIDATEOBJECT(items);
+ _ASSERTE(keys != NULL);
+
+ // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
+ // non-zero lower bounds. VB might care. </TODO>
+ if (keys->GetRank() != 1 || keys->GetLowerBoundsPtr()[0] != 0)
+ FC_RETURN_BOOL(FALSE);
+
+ _ASSERTE(left <= right);
+ _ASSERTE(right < keys->GetNumComponents() || keys->GetNumComponents() == 0);
+
+ TypeHandle keysTH = keys->GetArrayElementTypeHandle();
+ const CorElementType keysElType = keysTH.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType_NoThrow(keysElType))
+ FC_RETURN_BOOL(FALSE);
+ if (items != NULL) {
+ TypeHandle itemsTH = items->GetArrayElementTypeHandle();
+ if (keysTH != itemsTH)
+ FC_RETURN_BOOL(FALSE); // Can't currently handle sorting different types of arrays.
+ }
+
+ // Handle special case of a 0 element range to sort.
+ // Consider both Sort(array, x, x) and Sort(zeroLen, 0, zeroLen.Length-1);
+ if (left == right || right == 0xffffffff)
+ FC_RETURN_BOOL(TRUE);
+
+ switch(keysElType) {
+ case ELEMENT_TYPE_I1:
+ ArrayHelpers<I1>::IntrospectiveSort((I1*) keys->GetDataPtr(), (I1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ ArrayHelpers<U1>::IntrospectiveSort((U1*) keys->GetDataPtr(), (U1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_I2:
+ ArrayHelpers<I2>::IntrospectiveSort((I2*) keys->GetDataPtr(), (I2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ ArrayHelpers<U2>::IntrospectiveSort((U2*) keys->GetDataPtr(), (U2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_I4:
+ ArrayHelpers<I4>::IntrospectiveSort((I4*) keys->GetDataPtr(), (I4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_U4:
+ ArrayHelpers<U4>::IntrospectiveSort((U4*) keys->GetDataPtr(), (U4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_R4:
+ {
+ R4 * R4Keys = (R4*) keys->GetDataPtr();
+ R4 * R4Items = (R4*) (items == NULL ? NULL : items->GetDataPtr());
+
+ // Comparison to NaN is always false, so do a linear pass
+ // and swap all NaNs to the front of the array
+ left = ArrayHelpers<R4>::NaNPrepass(R4Keys, R4Items, left, right);
+ if(left != right) ArrayHelpers<R4>::IntrospectiveSort(R4Keys, R4Items, left, right);
+ break;
+ };
+
+ case ELEMENT_TYPE_I8:
+ ArrayHelpers<I8>::IntrospectiveSort((I8*) keys->GetDataPtr(), (I8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_U8:
+ ArrayHelpers<U8>::IntrospectiveSort((U8*) keys->GetDataPtr(), (U8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
+ break;
+
+ case ELEMENT_TYPE_R8:
+ {
+ R8 * R8Keys = (R8*) keys->GetDataPtr();
+ R8 * R8Items = (R8*) (items == NULL ? NULL : items->GetDataPtr());
+
+ // Comparison to NaN is always false, so do a linear pass
+ // and swap all NaNs to the front of the array
+ left = ArrayHelpers<R8>::NaNPrepass(R8Keys, R8Items, left, right);
+ if(left != right) ArrayHelpers<R8>::IntrospectiveSort(R8Keys, R8Items, left, right);
+ break;
+ };
+
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ // In V1.0, IntPtr & UIntPtr are not fully supported types. They do
+ // not implement IComparable, so searching & sorting for them should
+ // fail. In V1.1 or V2.0, this should change.
+ FC_RETURN_BOOL(FALSE);
+
+ default:
+ _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZSort");
+ FC_RETURN_BOOL(FALSE);
+ }
+ FC_RETURN_BOOL(TRUE);
+FCIMPLEND
+
+FCIMPL3(FC_BOOL_RET, ArrayHelper::TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+ _ASSERTE(array != NULL);
+
+ // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
+ // non-zero lower bounds. VB might care. </TODO>
+ if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
+ FC_RETURN_BOOL(FALSE);
+
+ _ASSERTE(index <= array->GetNumComponents());
+ _ASSERTE(count <= array->GetNumComponents());
+ _ASSERTE(array->GetNumComponents() >= index + count);
+
+ TypeHandle arrayTH = array->GetArrayElementTypeHandle();
+ const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
+ if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
+ FC_RETURN_BOOL(FALSE);
+
+ switch(arrayElType) {
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_BOOLEAN:
+ ArrayHelpers<U1>::Reverse((U1*) array->GetDataPtr(), index, count);
+ break;
+
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ ArrayHelpers<U2>::Reverse((U2*) array->GetDataPtr(), index, count);
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ IN_WIN32(case ELEMENT_TYPE_I:)
+ IN_WIN32(case ELEMENT_TYPE_U:)
+ ArrayHelpers<U4>::Reverse((U4*) array->GetDataPtr(), index, count);
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ IN_WIN64(case ELEMENT_TYPE_I:)
+ IN_WIN64(case ELEMENT_TYPE_U:)
+ ArrayHelpers<U8>::Reverse((U8*) array->GetDataPtr(), index, count);
+ break;
+
+ default:
+ _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZReverse");
+ FC_RETURN_BOOL(FALSE);
+ }
+ FC_RETURN_BOOL(TRUE);
+}
+FCIMPLEND
diff --git a/src/classlibnative/bcltype/arrayhelpers.h b/src/classlibnative/bcltype/arrayhelpers.h
new file mode 100644
index 0000000000..fdf9ce220c
--- /dev/null
+++ b/src/classlibnative/bcltype/arrayhelpers.h
@@ -0,0 +1,348 @@
+// 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.
+//
+// File: ArrayHelpers.h
+//
+
+//
+// Helper methods for the Array class
+// Specifically, this contains indexing, sorting & searching templates.
+
+
+#ifndef _ARRAYHELPERS_H_
+#define _ARRAYHELPERS_H_
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_) && !defined(FPO_ON)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#define FPO_ON 1
+#define COMARRAYHELPERS_TURNED_FPO_ON 1
+#endif
+
+#include "fcall.h"
+
+
+template <class KIND>
+class ArrayHelpers
+{
+public:
+ static int IndexOf(KIND array[], UINT32 index, UINT32 count, KIND value) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(array != NULL && index >= 0 && count >= 0);
+ for(UINT32 i=index; i<index+count; i++)
+ if (array[i] == value)
+ return i;
+ return -1;
+ }
+
+ static int LastIndexOf(KIND array[], UINT32 index, UINT32 count, KIND value) {
+ LIMITED_METHOD_CONTRACT;
+
+ INT32 startIndex = (INT32)index;
+ INT32 n = (INT32)count;
+ _ASSERTE(array != NULL);
+ _ASSERTE(startIndex >= 0);
+ _ASSERTE(n >= 0);
+
+ // Note (startIndex- n) may be -1 when startIndex is 0 and n is 1.
+ _ASSERTE(startIndex >= n - 1);
+
+ // Prefast: caller asserts guarantee that startIndex - n won't underflow, but we need to spell
+ // this out for prefast.
+ PREFIX_ASSUME(startIndex >= startIndex - n);
+ INT32 endIndex = max(startIndex - n, -1);
+
+ for(INT32 i=startIndex; i> endIndex; i--)
+ if (array[i] == value)
+ return i;
+ return -1;
+ }
+
+ static int BinarySearchBitwiseEquals(KIND array[], int index, int length, KIND value) {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(array != NULL);
+ _ASSERTE(length >= 0);
+ _ASSERTE(index >= 0);
+
+ int lo = index;
+
+ // Prefast: mscorlib.dll!System.Array.BinarySearch(Array,int,int,Object,IComparer)
+ // guarantees index and length are in the array bounds and do not overflow
+ PREFIX_ASSUME(index >= 0 && length >= 0 && INT32_MAX >= index + length - 1);
+ int hi = index + length - 1;
+
+ // Note: if length == 0, hi will be Int32.MinValue, and our comparison
+ // here between 0 & -1 will prevent us from breaking anything.
+ while (lo <= hi) {
+ int i = lo + ((hi - lo) >> 1);
+ if (array[i] < value) {
+ lo = i + 1;
+ }
+ else if (array[i] > value){
+ hi = i - 1;
+ }
+ else {
+ return i;
+ }
+ }
+ return ~lo;
+ }
+
+ inline static void SwapIfGreaterWithItems(KIND keys[], KIND items[], int a, int b) {
+ if (a != b) {
+ if (keys[a] > keys[b]) {
+ KIND key = keys[a];
+ keys[a] = keys[b];
+ keys[b] = key;
+ if (items != NULL) {
+ KIND item = items[a];
+ items[a] = items[b];
+ items[b] = item;
+ }
+ }
+ }
+ }
+
+ // For sorting, move all NaN instances to front of the input array
+ template <class REAL>
+ static unsigned int NaNPrepass(REAL keys[], REAL items[], unsigned int left, unsigned int right) {
+ for (unsigned int i = left; i <= right; i++) {
+ if (_isnan(keys[i])) {
+ REAL temp = keys[left];
+ keys[left] = keys[i];
+ keys[i] = temp;
+ if (items != NULL) {
+ temp = items[left];
+ items[left] = items[i];
+ items[i] = temp;
+ }
+ left++;
+ }
+ }
+ return left;
+ }
+
+ // Implementation of Introspection Sort
+ static void IntrospectiveSort(KIND keys[], KIND items[], int left, int right) {
+ WRAPPER_NO_CONTRACT;
+
+ // Make sure left != right in your own code.
+ _ASSERTE(keys != NULL && left < right);
+
+ int length = right - left + 1;
+
+ if (length < 2)
+ return;
+
+ IntroSort(keys, items, left, right, 2 * FloorLog2(length));
+ }
+
+ static const int introsortSizeThreshold = 16;
+
+ static int FloorLog2(int n)
+ {
+ int result = 0;
+ while (n >= 1)
+ {
+ result++;
+ n = n / 2;
+ }
+ return result;
+ }
+
+ static void IntroSort(KIND keys[], KIND items[], int lo, int hi, int depthLimit)
+ {
+ while (hi > lo)
+ {
+ int partitionSize = hi - lo + 1;
+ if(partitionSize <= introsortSizeThreshold)
+ {
+ if (partitionSize == 1)
+ {
+ return;
+ }
+ if (partitionSize == 2)
+ {
+ SwapIfGreaterWithItems(keys, items, lo, hi);
+ return;
+ }
+ if (partitionSize == 3)
+ {
+ SwapIfGreaterWithItems(keys, items, lo, hi-1);
+ SwapIfGreaterWithItems(keys, items, lo, hi);
+ SwapIfGreaterWithItems(keys, items, hi-1, hi);
+ return;
+ }
+
+ InsertionSort(keys, items, lo, hi);
+ return;
+ }
+
+ if (depthLimit == 0)
+ {
+ Heapsort(keys, items, lo, hi);
+ return;
+ }
+ depthLimit--;
+
+ int p = PickPivotAndPartition(keys, items, lo, hi);
+ IntroSort(keys, items, p + 1, hi, depthLimit);
+ hi = p - 1;
+ }
+ return;
+ }
+
+ static void Swap(KIND keys[], KIND items[], int i, int j)
+ {
+ KIND t = keys[i];
+ keys[i] = keys[j];
+ keys[j] = t;
+
+ if (items != NULL)
+ {
+ KIND item = items[i];
+ items[i] = items[j];
+ items[j] = item;
+ }
+ }
+
+ static int PickPivotAndPartition(KIND keys[], KIND items[], int lo, int hi)
+ {
+ // Compute median-of-three. But also partition them, since we've done the comparison.
+ int mid = lo + (hi - lo) / 2;
+
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
+ SwapIfGreaterWithItems(keys, items, lo, mid);
+ SwapIfGreaterWithItems(keys, items, lo, hi);
+ SwapIfGreaterWithItems(keys, items, mid, hi);
+
+ KIND pivot = keys[mid];
+ Swap(keys, items, mid, hi - 1);
+
+ int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+
+ while (left < right)
+ {
+ while (left < (hi - 1) && keys[++left] < pivot);
+ while (right > lo && pivot < keys[--right]);
+
+ if ((left >= right))
+ break;
+
+ Swap(keys, items, left, right);
+ }
+
+ // Put pivot in the right location.
+ Swap(keys, items, left, (hi - 1));
+ return left;
+ }
+
+ static void Heapsort(KIND keys[], KIND items[], int lo, int hi)
+ {
+ int n = hi - lo + 1;
+ for (int i = n / 2; i >= 1; i = i - 1)
+ {
+ DownHeap(keys, items, i, n, lo);
+ }
+ for (int i = n; i > 1; i = i - 1)
+ {
+ Swap(keys, items, lo, lo + i -1);
+ DownHeap(keys, items, 1, i - 1, lo);
+ }
+ }
+
+ static void DownHeap(KIND keys[], KIND items[], int i, int n, int lo)
+ {
+ KIND d = keys[lo + i - 1];
+ KIND di = (items != NULL) ? items[lo + i - 1] : NULL;
+ int child;
+
+ while (i <= n / 2)
+ {
+ child = 2 * i;
+ if (child < n && keys[lo + child - 1] < keys[lo + child])
+ {
+ child++;
+ }
+ if (!(d < keys[lo + child - 1]))
+ break;
+
+ keys[lo + i - 1] = keys[lo + child - 1];
+ if(items != NULL)
+ items[lo + i - 1] = items[ lo + child - 1];
+ i = child;
+ }
+ keys[lo + i - 1] = d;
+ if(items != NULL)
+ items[lo + i - 1] = di;
+ }
+
+ static void InsertionSort(KIND keys[], KIND items[], int lo, int hi)
+ {
+ int i, j;
+ KIND t, ti = NULL;
+ for (i = lo; i < hi; i++)
+ {
+ j = i;
+ t = keys[i + 1];
+ if(items != NULL)
+ ti = items[i + 1];
+ while (j >= lo && t < keys[j])
+ {
+ keys[j + 1] = keys[j];
+ if(items != NULL)
+ items[j + 1] = items[j];
+ j--;
+ }
+ keys[j + 1] = t;
+ if(items != NULL)
+ items[j + 1] = ti;
+ }
+ }
+
+ static void Reverse(KIND array[], UINT32 index, UINT32 count) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(array != NULL);
+ if (count == 0) {
+ return;
+ }
+ UINT32 i = index;
+ UINT32 j = index + count - 1;
+ while(i < j) {
+ KIND temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ i++;
+ j--;
+ }
+ }
+};
+
+
+class ArrayHelper
+{
+public:
+ // These methods return TRUE or FALSE for success or failure, and the real
+ // result is an out param. They're helpers to make operations on SZ arrays of
+ // primitives significantly faster.
+ static FCDECL5(FC_BOOL_RET, TrySZIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
+ static FCDECL5(FC_BOOL_RET, TrySZLastIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
+ static FCDECL5(FC_BOOL_RET, TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
+
+ static FCDECL4(FC_BOOL_RET, TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right);
+ static FCDECL3(FC_BOOL_RET, TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count);
+
+ // Helper methods
+ static INT32 IndexOfUINT8( UINT8* array, UINT32 index, UINT32 count, UINT8 value);
+};
+
+#if defined(COMARRAYHELPERS_TURNED_FPO_ON)
+#pragma optimize("", on) // Go back to command line default optimizations
+#undef COMARRAYHELPERS_TURNED_FPO_ON
+#undef FPO_ON
+#endif
+
+#endif // _ARRAYHELPERS_H_
diff --git a/src/classlibnative/bcltype/arraynative.cpp b/src/classlibnative/bcltype/arraynative.cpp
new file mode 100644
index 0000000000..b1aa6f8751
--- /dev/null
+++ b/src/classlibnative/bcltype/arraynative.cpp
@@ -0,0 +1,1562 @@
+// 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.
+//
+// File: ArrayNative.cpp
+//
+
+//
+// This file contains the native methods that support the Array class
+//
+
+
+#include "common.h"
+#include "arraynative.h"
+#include "excep.h"
+#include "field.h"
+#include "security.h"
+#include "invokeutil.h"
+
+FCIMPL1(INT32, ArrayNative::GetRank, ArrayBase* array)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array == NULL)
+ FCThrow(kNullReferenceException);
+
+ return array->GetRank();
+}
+FCIMPLEND
+
+
+FCIMPL2(INT32, ArrayNative::GetLowerBound, ArrayBase* array, unsigned int dimension)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array == NULL)
+ FCThrow(kNullReferenceException);
+
+ if (dimension != 0)
+ {
+ // Check the dimension is within our rank
+ unsigned int rank = array->GetRank();
+
+ if (dimension >= rank)
+ FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex"));
+ }
+
+ return array->GetLowerBoundsPtr()[dimension];
+}
+FCIMPLEND
+
+
+// Get inclusive upper bound
+FCIMPL2(INT32, ArrayNative::GetUpperBound, ArrayBase* array, unsigned int dimension)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array == NULL)
+ FCThrow(kNullReferenceException);
+
+ if (dimension != 0)
+ {
+ // Check the dimension is within our rank
+ unsigned int rank = array->GetRank();
+
+ if (dimension >= rank)
+ FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex"));
+ }
+
+ return array->GetBoundsPtr()[dimension] + array->GetLowerBoundsPtr()[dimension] - 1;
+}
+FCIMPLEND
+
+
+FCIMPL2(INT32, ArrayNative::GetLength, ArrayBase* array, unsigned int dimension)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array==NULL)
+ FCThrow(kNullReferenceException);
+
+ if (dimension != 0)
+ {
+ // Check the dimension is within our rank
+ unsigned int rank = array->GetRank();
+ if (dimension >= rank)
+ FCThrow(kIndexOutOfRangeException);
+ }
+
+ return array->GetBoundsPtr()[dimension];
+}
+FCIMPLEND
+
+
+FCIMPL1(INT32, ArrayNative::GetLengthNoRank, ArrayBase* array)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array==NULL)
+ FCThrow(kNullReferenceException);
+
+ SIZE_T numComponents = array->GetNumComponents();
+ if (numComponents > INT32_MAX)
+ FCThrow(kOverflowException);
+
+ return (INT32)numComponents;
+}
+FCIMPLEND
+
+
+FCIMPL1(INT64, ArrayNative::GetLongLengthNoRank, ArrayBase* array)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array==NULL)
+ FCThrow(kNullReferenceException);
+
+ return array->GetNumComponents();
+}
+FCIMPLEND
+
+
+FCIMPL1(INT32, ArrayNative::GetDataPtrOffsetInternal, ArrayBase* array)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(array);
+
+ if (array == NULL)
+ FCThrow(kNullReferenceException);
+
+ return ArrayBase::GetDataPtrOffset(array->GetMethodTable());
+}
+FCIMPLEND
+
+
+
+
+
+
+// array is GC protected by caller
+void ArrayInitializeWorker(ARRAYBASEREF * arrayRef,
+ MethodTable* pArrayMT,
+ MethodTable* pElemMT)
+{
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_SO_INTOLERANT;
+
+ // Ensure that the array element type is fully loaded before executing its code
+ pElemMT->EnsureInstanceActive();
+
+ //can not use contract here because of SEH
+ _ASSERTE(IsProtectedByGCFrame (arrayRef));
+
+ SIZE_T offset = ArrayBase::GetDataPtrOffset(pArrayMT);
+ SIZE_T size = pArrayMT->GetComponentSize();
+ SIZE_T cElements = (*arrayRef)->GetNumComponents();
+
+ MethodTable * pCanonMT = pElemMT->GetCanonicalMethodTable();
+ WORD slot = pCanonMT->GetDefaultConstructorSlot();
+
+ PCODE ctorFtn = pCanonMT->GetSlot(slot);
+
+#ifdef _X86_
+ BEGIN_CALL_TO_MANAGED();
+
+ typedef void (__fastcall * CtorFtnType)(BYTE*, BYTE*);
+
+ for (SIZE_T i = 0; i < cElements; i++)
+ {
+ // Since GetSlot() is not idempotent and may have returned
+ // a non-optimal entry-point the first time round.
+ if (i == 1)
+ {
+ ctorFtn = pCanonMT->GetSlot(slot);
+ }
+
+ BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset);
+
+#ifdef _DEBUG
+ __asm {
+ mov ECX, thisPtr
+ mov EDX, pElemMT // Instantiation argument if the type is generic
+ call [ctorFtn]
+ nop // Mark the fact that we can call managed code
+ }
+#else // _DEBUG
+ (*(CtorFtnType)ctorFtn)(thisPtr, (BYTE*)pElemMT);
+#endif // _DEBUG
+
+ offset += size;
+ }
+
+ END_CALL_TO_MANAGED();
+#else // _X86_
+ //
+ // This is quite a bit slower, but it is portable.
+ //
+
+ for (SIZE_T i =0; i < cElements; i++)
+ {
+ // Since GetSlot() is not idempotent and may have returned
+ // a non-optimal entry-point the first time round.
+ if (i == 1)
+ {
+ ctorFtn = pCanonMT->GetSlot(slot);
+ }
+
+ BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset);
+
+ PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(ctorFtn);
+ DECLARE_ARGHOLDER_ARRAY(args, 2);
+ args[ARGNUM_0] = PTR_TO_ARGHOLDER(thisPtr);
+ args[ARGNUM_1] = PTR_TO_ARGHOLDER(pElemMT); // Instantiation argument if the type is generic
+ CALL_MANAGED_METHOD_NORET(args);
+
+ offset += size;
+ }
+#endif // _X86_
+}
+
+
+FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array)
+{
+ FCALL_CONTRACT;
+
+ if (array == NULL)
+ {
+ FCThrowVoid(kNullReferenceException);
+ }
+
+
+ MethodTable* pArrayMT = array->GetMethodTable();
+
+ TypeHandle thElem = pArrayMT->GetApproxArrayElementTypeHandle();
+ if (thElem.IsTypeDesc())
+ return;
+
+ MethodTable * pElemMT = thElem.AsMethodTable();
+ if (!pElemMT->HasDefaultConstructor() || !pElemMT->IsValueType())
+ return;
+
+ ARRAYBASEREF arrayRef (array);
+ HELPER_METHOD_FRAME_BEGIN_1(arrayRef);
+
+ ArrayInitializeWorker(&arrayRef, pArrayMT, pElemMT);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+
+
+
+
+ // Returns an enum saying whether you can copy an array of srcType into destType.
+ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(pDest != NULL);
+ }
+ CONTRACTL_END;
+
+ // The next 50 lines are a little tricky. Change them with great care.
+ //
+
+ // This first bit is a minor optimization: e.g. when copying byte[] to byte[]
+ // we do not need to call GetArrayElementTypeHandle().
+ MethodTable *pSrcMT = pSrc->GetMethodTable();
+ MethodTable *pDestMT = pDest->GetMethodTable();
+ if (pSrcMT == pDestMT)
+ return AssignWillWork;
+
+ TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
+ TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
+ if (srcTH == destTH) // This check kicks for different array kind or dimensions
+ return AssignWillWork;
+
+ // Value class boxing
+ if (srcTH.IsValueType() && !destTH.IsValueType())
+ {
+ switch (srcTH.CanCastToNoGC(destTH))
+ {
+ case TypeHandle::CanCast : return AssignBoxValueClassOrPrimitive;
+ case TypeHandle::CannotCast : return AssignWrongType;
+ default : return AssignDontKnow;
+ }
+ }
+
+ // Value class unboxing.
+ if (!srcTH.IsValueType() && destTH.IsValueType())
+ {
+ if (srcTH.CanCastToNoGC(destTH) == TypeHandle::CanCast)
+ return AssignUnboxValueClass;
+ else if (destTH.CanCastToNoGC(srcTH) == TypeHandle::CanCast) // V extends IV. Copying from IV to V, or Object to V.
+ return AssignUnboxValueClass;
+ else
+ return AssignDontKnow;
+ }
+
+ const CorElementType srcElType = srcTH.GetSignatureCorElementType();
+ const CorElementType destElType = destTH.GetSignatureCorElementType();
+ _ASSERTE(srcElType < ELEMENT_TYPE_MAX);
+ _ASSERTE(destElType < ELEMENT_TYPE_MAX);
+
+ // Copying primitives from one type to another
+ if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
+ {
+ if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
+ return AssignPrimitiveWiden;
+ else
+ return AssignWrongType;
+ }
+
+ // dest Object extends src
+ if (srcTH.CanCastToNoGC(destTH) == TypeHandle::CanCast)
+ return AssignWillWork;
+
+ // src Object extends dest
+ if (destTH.CanCastToNoGC(srcTH) == TypeHandle::CanCast)
+ return AssignMustCast;
+
+ // class X extends/implements src and implements dest.
+ if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE)
+ return AssignMustCast;
+
+ // class X implements src and extends/implements dest
+ if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE)
+ return AssignMustCast;
+
+ // Enum is stored as a primitive of type dest.
+ if (srcTH.IsEnum() && srcTH.GetInternalCorElementType() == destElType)
+ return AssignWillWork;
+
+ return AssignDontKnow;
+}
+
+
+// Returns an enum saying whether you can copy an array of srcType into destType.
+ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(pDest != NULL);
+ }
+ CONTRACTL_END;
+
+ // The next 50 lines are a little tricky. Change them with great care.
+ //
+
+ // This first bit is a minor optimization: e.g. when copying byte[] to byte[]
+ // we do not need to call GetArrayElementTypeHandle().
+ MethodTable *pSrcMT = pSrc->GetMethodTable();
+ MethodTable *pDestMT = pDest->GetMethodTable();
+ if (pSrcMT == pDestMT)
+ return AssignWillWork;
+
+ TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
+ TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
+ if (srcTH == destTH) // This check kicks for different array kind or dimensions
+ return AssignWillWork;
+
+ // Value class boxing
+ if (srcTH.IsValueType() && !destTH.IsValueType())
+ {
+ if (srcTH.CanCastTo(destTH))
+ return AssignBoxValueClassOrPrimitive;
+ else
+ return AssignWrongType;
+ }
+
+ // Value class unboxing.
+ if (!srcTH.IsValueType() && destTH.IsValueType())
+ {
+ if (srcTH.CanCastTo(destTH))
+ return AssignUnboxValueClass;
+ else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V.
+ return AssignUnboxValueClass;
+ else
+ return AssignWrongType;
+ }
+
+ const CorElementType srcElType = srcTH.GetSignatureCorElementType();
+ const CorElementType destElType = destTH.GetSignatureCorElementType();
+ _ASSERTE(srcElType < ELEMENT_TYPE_MAX);
+ _ASSERTE(destElType < ELEMENT_TYPE_MAX);
+
+ // Copying primitives from one type to another
+ if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
+ {
+ if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
+ return AssignPrimitiveWiden;
+ else
+ return AssignWrongType;
+ }
+
+ // dest Object extends src
+ if (srcTH.CanCastTo(destTH))
+ return AssignWillWork;
+
+ // src Object extends dest
+ if (destTH.CanCastTo(srcTH))
+ return AssignMustCast;
+
+ // class X extends/implements src and implements dest.
+ if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE)
+ return AssignMustCast;
+
+ // class X implements src and extends/implements dest
+ if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE)
+ return AssignMustCast;
+
+ // Enum is stored as a primitive of type dest.
+ if (srcTH.IsEnum() && srcTH.GetInternalCorElementType() == destElType)
+ return AssignWillWork;
+
+ return AssignWrongType;
+}
+
+
+// Casts and assigns each element of src array to the dest array type.
+void ArrayNative::CastCheckEachElement(const BASEARRAYREF pSrcUnsafe, const unsigned int srcIndex, BASEARRAYREF pDestUnsafe, unsigned int destIndex, const unsigned int len)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pSrcUnsafe != NULL);
+ PRECONDITION(srcIndex >= 0);
+ PRECONDITION(pDestUnsafe != NULL);
+ PRECONDITION(len > 0);
+ }
+ CONTRACTL_END;
+
+ // pSrc is either a PTRARRAYREF or a multidimensional array.
+ TypeHandle destTH = pDestUnsafe->GetArrayElementTypeHandle();
+
+ struct _gc
+ {
+ OBJECTREF obj;
+ BASEARRAYREF pDest;
+ BASEARRAYREF pSrc;
+ } gc;
+
+ gc.obj = NULL;
+ gc.pDest = pDestUnsafe;
+ gc.pSrc = pSrcUnsafe;
+
+ GCPROTECT_BEGIN(gc);
+
+ for(unsigned int i=srcIndex; i<srcIndex + len; ++i)
+ {
+ gc.obj = ObjectToOBJECTREF(*((Object**) gc.pSrc->GetDataPtr() + i));
+
+ // Now that we have grabbed obj, we are no longer subject to races from another
+ // mutator thread.
+ if (gc.obj != NULL && !ObjIsInstanceOf(OBJECTREFToObject(gc.obj), destTH))
+ COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement"));
+
+ OBJECTREF * destData = (OBJECTREF*)(gc.pDest->GetDataPtr()) + i - srcIndex + destIndex;
+ SetObjectReference(destData, gc.obj, gc.pDest->GetAppDomain());
+ }
+
+ GCPROTECT_END();
+
+ return;
+}
+
+
+// Will box each element in an array of value classes or primitives into an array of Objects.
+void ArrayNative::BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(srcIndex >= 0);
+ PRECONDITION(pDest != NULL);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+ // pDest is either a PTRARRAYREF or a multidimensional array.
+ _ASSERTE(pSrc!=NULL && srcIndex>=0 && pDest!=NULL && destIndex>=0 && length>=0);
+ TypeHandle srcTH = pSrc->GetArrayElementTypeHandle();
+#ifdef _DEBUG
+ TypeHandle destTH = pDest->GetArrayElementTypeHandle();
+#endif
+ _ASSERTE(srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pSrc->GetArrayElementType()));
+ _ASSERTE(!destTH.GetMethodTable()->IsValueType());
+
+ // Get method table of type we're copying from - we need to allocate objects of that type.
+ MethodTable * pSrcMT = srcTH.GetMethodTable();
+ PREFIX_ASSUME(pSrcMT != NULL);
+
+ if (!pSrcMT->IsClassInited())
+ {
+ BASEARRAYREF pSrcTmp = pSrc;
+ BASEARRAYREF pDestTmp = pDest;
+ GCPROTECT_BEGIN (pSrcTmp);
+ GCPROTECT_BEGIN (pDestTmp);
+ pSrcMT->CheckRunClassInitThrowing();
+ pSrc = pSrcTmp;
+ pDest = pDestTmp;
+ GCPROTECT_END ();
+ GCPROTECT_END ();
+ }
+
+ const unsigned int srcSize = pSrcMT->GetNumInstanceFieldBytes();
+ unsigned int srcArrayOffset = srcIndex * srcSize;
+
+ struct _gc
+ {
+ BASEARRAYREF src;
+ BASEARRAYREF dest;
+ OBJECTREF obj;
+ } gc;
+
+ gc.src = pSrc;
+ gc.dest = pDest;
+ gc.obj = NULL;
+
+ void* srcPtr = 0;
+ GCPROTECT_BEGIN(gc);
+ GCPROTECT_BEGININTERIOR(srcPtr);
+ for (unsigned int i=destIndex; i < destIndex+length; i++, srcArrayOffset += srcSize)
+ {
+ srcPtr = (BYTE*)gc.src->GetDataPtr() + srcArrayOffset;
+ gc.obj = pSrcMT->FastBox(&srcPtr);
+
+ OBJECTREF * destData = (OBJECTREF*)((gc.dest)->GetDataPtr()) + i;
+ SetObjectReference(destData, gc.obj, gc.dest->GetAppDomain());
+ }
+ GCPROTECT_END();
+ GCPROTECT_END();
+}
+
+
+// Unboxes from an Object[] into a value class or primitive array.
+void ArrayNative::UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(srcIndex >= 0);
+ PRECONDITION(pDest != NULL);
+ PRECONDITION(destIndex >= 0);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ TypeHandle srcTH = pSrc->GetArrayElementTypeHandle();
+#endif
+ TypeHandle destTH = pDest->GetArrayElementTypeHandle();
+ _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType()));
+ _ASSERTE(!srcTH.GetMethodTable()->IsValueType());
+
+ MethodTable * pDestMT = destTH.GetMethodTable();
+ PREFIX_ASSUME(pDestMT != NULL);
+
+ const unsigned int destSize = pDestMT->GetNumInstanceFieldBytes();
+ BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * sizeof(OBJECTREF);
+ BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize;
+
+ for(; length>0; length--, srcData += sizeof(OBJECTREF), data += destSize)
+ {
+ OBJECTREF obj = ObjectToOBJECTREF(*(Object**)srcData);
+
+ // Now that we have retrieved the element, we are no longer subject to race
+ // conditions from another array mutator.
+
+ if (!pDestMT->UnBoxInto(data, obj))
+ goto fail;
+ }
+ return;
+
+fail:
+ COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement"));
+}
+
+
+// Widen primitive types to another primitive type.
+void ArrayNative::PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(srcIndex >= 0);
+ PRECONDITION(pDest != NULL);
+ PRECONDITION(destIndex >= 0);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+ // Get appropriate sizes, which requires method tables.
+ TypeHandle srcTH = pSrc->GetArrayElementTypeHandle();
+ TypeHandle destTH = pDest->GetArrayElementTypeHandle();
+
+ const CorElementType srcElType = srcTH.GetSignatureCorElementType();
+ const CorElementType destElType = destTH.GetSignatureCorElementType();
+ const unsigned int srcSize = GetSizeForCorElementType(srcElType);
+ const unsigned int destSize = GetSizeForCorElementType(destElType);
+
+ BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * srcSize;
+ BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize;
+
+ _ASSERTE(srcElType != destElType); // We shouldn't be here if these are the same type.
+ _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType));
+
+ for(; length>0; length--, srcData += srcSize, data += destSize)
+ {
+ // We pretty much have to do some fancy datatype mangling every time here, for
+ // converting w/ sign extension and floating point conversions.
+ switch (srcElType)
+ {
+ case ELEMENT_TYPE_U1:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_R4:
+ *(float*)data = *(UINT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(UINT8*)srcData;
+ break;
+#ifndef BIGENDIAN
+ default:
+ *(UINT8*)data = *(UINT8*)srcData;
+ memset(data+1, 0, destSize - 1);
+ break;
+#else // BIGENDIAN
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ *(INT16*)data = *(UINT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ *(INT32*)data = *(UINT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ *(INT64*)data = *(UINT8*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion");
+#endif // BIGENDIAN
+ }
+ break;
+
+
+ case ELEMENT_TYPE_I1:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_I2:
+ *(INT16*)data = *(INT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I4:
+ *(INT32*)data = *(INT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ *(INT64*)data = *(INT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R4:
+ *(float*)data = *(INT8*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(INT8*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from I1 to another type hit unsupported widening conversion");
+ }
+ break;
+
+
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_R4:
+ *(float*)data = *(UINT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(UINT16*)srcData;
+ break;
+#ifndef BIGENDIAN
+ default:
+ *(UINT16*)data = *(UINT16*)srcData;
+ memset(data+2, 0, destSize - 2);
+ break;
+#else // BIGENDIAN
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+ *(UINT16*)data = *(UINT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ *(UINT32*)data = *(UINT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ *(UINT64*)data = *(UINT16*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion");
+#endif // BIGENDIAN
+ }
+ break;
+
+
+ case ELEMENT_TYPE_I2:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_I4:
+ *(INT32*)data = *(INT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ *(INT64*)data = *(INT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R4:
+ *(float*)data = *(INT16*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(INT16*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from I2 to another type hit unsupported widening conversion");
+ }
+ break;
+
+
+ case ELEMENT_TYPE_I4:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_I8:
+ *(INT64*)data = *(INT32*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R4:
+ *(float*)data = (float)*(INT32*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(INT32*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from I4 to another type hit unsupported widening conversion");
+ }
+ break;
+
+
+ case ELEMENT_TYPE_U4:
+ switch (destElType)
+ {
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ *(INT64*)data = *(UINT32*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R4:
+ *(float*)data = (float)*(UINT32*)srcData;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ *(double*)data = *(UINT32*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Array.Copy from U4 to another type hit unsupported widening conversion");
+ }
+ break;
+
+
+ case ELEMENT_TYPE_I8:
+ if (destElType == ELEMENT_TYPE_R4)
+ {
+ *(float*) data = (float) *(INT64*)srcData;
+ }
+ else
+ {
+ _ASSERTE(destElType==ELEMENT_TYPE_R8);
+ *(double*) data = (double) *(INT64*)srcData;
+ }
+ break;
+
+
+ case ELEMENT_TYPE_U8:
+ if (destElType == ELEMENT_TYPE_R4)
+ {
+ //*(float*) data = (float) *(UINT64*)srcData;
+ INT64 srcVal = *(INT64*)srcData;
+ float f = (float) srcVal;
+ if (srcVal < 0)
+ f += 4294967296.0f * 4294967296.0f; // This is 2^64
+
+ *(float*) data = f;
+ }
+ else
+ {
+ _ASSERTE(destElType==ELEMENT_TYPE_R8);
+ //*(double*) data = (double) *(UINT64*)srcData;
+ INT64 srcVal = *(INT64*)srcData;
+ double d = (double) srcVal;
+ if (srcVal < 0)
+ d += 4294967296.0 * 4294967296.0; // This is 2^64
+
+ *(double*) data = d;
+ }
+ break;
+
+
+ case ELEMENT_TYPE_R4:
+ *(double*) data = *(float*)srcData;
+ break;
+
+ default:
+ _ASSERTE(!"Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!");
+ }
+ }
+}
+
+//
+// This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are
+// updated atomically.
+//
+// The CRT version of memmove does not always guarantee that updates of aligned fields stay atomic (e.g. it is using "rep movsb" in some cases).
+// Type safety guarantees and background GC scanning requires object references in GC heap to be updated atomically.
+//
+void memmoveGCRefs(void *dest, const void *src, size_t len)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(dest));
+ PRECONDITION(CheckPointer(src));
+ PRECONDITION(len >= 0);
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ // Make sure everything is pointer aligned
+ _ASSERTE(IS_ALIGNED(dest, sizeof(SIZE_T)));
+ _ASSERTE(IS_ALIGNED(src, sizeof(SIZE_T)));
+ _ASSERTE(IS_ALIGNED(len, sizeof(SIZE_T)));
+
+ size_t size = len;
+ BYTE * dmem = (BYTE *)dest;
+ BYTE * smem = (BYTE *)src;
+
+ GCHeapMemoryBarrier();
+
+ if (dmem <= smem || smem + size <= dmem)
+ {
+ // copy 16 bytes at a time
+ while (size >= 4 * sizeof(SIZE_T))
+ {
+ size -= 4 * sizeof(SIZE_T);
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1];
+ ((SIZE_T *)dmem)[2] = ((SIZE_T *)smem)[2];
+ ((SIZE_T *)dmem)[3] = ((SIZE_T *)smem)[3];
+ smem += 4 * sizeof(SIZE_T);
+ dmem += 4 * sizeof(SIZE_T);
+ }
+
+ if ((size & (2 * sizeof(SIZE_T))) != 0)
+ {
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1];
+ smem += 2 * sizeof(SIZE_T);
+ dmem += 2 * sizeof(SIZE_T);
+ }
+
+ if ((size & sizeof(SIZE_T)) != 0)
+ {
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ }
+ }
+ else
+ {
+ smem += size;
+ dmem += size;
+
+ // copy 16 bytes at a time
+ while (size >= 4 * sizeof(SIZE_T))
+ {
+ size -= 4 * sizeof(SIZE_T);
+ smem -= 4 * sizeof(SIZE_T);
+ dmem -= 4 * sizeof(SIZE_T);
+ ((SIZE_T *)dmem)[3] = ((SIZE_T *)smem)[3];
+ ((SIZE_T *)dmem)[2] = ((SIZE_T *)smem)[2];
+ ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1];
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ }
+
+ if ((size & (2 * sizeof(SIZE_T))) != 0)
+ {
+ smem -= 2 * sizeof(SIZE_T);
+ dmem -= 2 * sizeof(SIZE_T);
+ ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1];
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ }
+
+ if ((size & sizeof(SIZE_T)) != 0)
+ {
+ smem -= sizeof(SIZE_T);
+ dmem -= sizeof(SIZE_T);
+ ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0];
+ }
+ }
+
+ GCHeap::GetGCHeap()->SetCardsAfterBulkCopy((Object**)dest, len);
+}
+
+void ArrayNative::ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(pSrc != NULL);
+ PRECONDITION(srcIndex >= 0);
+ PRECONDITION(pDest != NULL);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+ BYTE *src = (BYTE*)pSrc->GetDataPtr();
+ BYTE *dst = (BYTE*)pDest->GetDataPtr();
+ SIZE_T size = pSrc->GetComponentSize();
+
+ src += srcIndex * size;
+ dst += destIndex * size;
+
+ if (pDest->GetMethodTable()->ContainsPointers())
+ {
+ memmoveGCRefs(dst, src, length * size);
+ }
+ else
+ {
+ memmove(dst, src, length * size);
+ }
+}
+
+FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable)
+{
+ FCALL_CONTRACT;
+
+ BYTE *src;
+ BYTE *dst;
+ SIZE_T size;
+
+ struct _gc
+ {
+ BASEARRAYREF pSrc;
+ BASEARRAYREF pDst;
+ } gc;
+
+ gc.pSrc = (BASEARRAYREF)m_pSrc;
+ gc.pDst = (BASEARRAYREF)m_pDst;
+
+ //
+ // creating a HelperMethodFrame is quite expensive,
+ // so we want to delay this for the most common case which doesn't trigger a GC.
+ // FCThrow is needed to throw an exception without a HelperMethodFrame
+ //
+
+ // cannot pass null for source or destination
+ if (gc.pSrc == NULL || gc.pDst == NULL) {
+ FCThrowArgumentNullVoid(gc.pSrc==NULL ? W("source") : W("dest"));
+ }
+
+ // source and destination must be arrays
+ _ASSERTE(gc.pSrc->GetMethodTable()->IsArray());
+ _ASSERTE(gc.pDst->GetMethodTable()->IsArray());
+
+ // Equal method tables should imply equal rank
+ _ASSERTE(!(gc.pSrc->GetMethodTable() == gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()));
+
+ // Which enables us to avoid touching the EEClass in simple cases
+ if (gc.pSrc->GetMethodTable() != gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()) {
+ FCThrowResVoid(kRankException, W("Rank_MustMatch"));
+ }
+
+ g_IBCLogger.LogMethodTableAccess(gc.pSrc->GetMethodTable());
+ g_IBCLogger.LogMethodTableAccess(gc.pDst->GetMethodTable());
+
+ int srcLB = gc.pSrc->GetLowerBoundsPtr()[0];
+ int destLB = gc.pDst->GetLowerBoundsPtr()[0];
+ // array bounds checking
+ const unsigned int srcLen = gc.pSrc->GetNumComponents();
+ const unsigned int destLen = gc.pDst->GetNumComponents();
+ if (m_iLength < 0)
+ FCThrowArgumentOutOfRangeVoid(W("length"), W("ArgumentOutOfRange_NeedNonNegNum"));
+
+ if (m_iSrcIndex < srcLB || (m_iSrcIndex - srcLB < 0))
+ FCThrowArgumentOutOfRangeVoid(W("srcIndex"), W("ArgumentOutOfRange_ArrayLB"));
+
+ if (m_iDstIndex < destLB || (m_iDstIndex - destLB < 0))
+ FCThrowArgumentOutOfRangeVoid(W("dstIndex"), W("ArgumentOutOfRange_ArrayLB"));
+
+ if ((DWORD)(m_iSrcIndex - srcLB + m_iLength) > srcLen)
+ FCThrowResVoid(kArgumentException, W("Arg_LongerThanSrcArray"));
+
+ if ((DWORD)(m_iDstIndex - destLB + m_iLength) > destLen)
+ FCThrowResVoid(kArgumentException, W("Arg_LongerThanDestArray"));
+
+ int r = 0;
+
+ // Small perf optimization - we copy from one portion of an array back to
+ // itself a lot when resizing collections, etc. The cost of doing the type
+ // checking is significant for copying small numbers of bytes (~half of the time
+ // for copying 1 byte within one array from element 0 to element 1).
+ if (gc.pSrc == gc.pDst)
+ r = AssignWillWork;
+ else
+ r = CanAssignArrayTypeNoGC(gc.pSrc, gc.pDst);
+
+ if (r == AssignWrongType) {
+ FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
+ }
+
+ if (r == AssignWillWork) {
+ if (m_iLength > 0)
+ ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+
+ FC_GC_POLL();
+ return;
+ }
+ else if (reliable) {
+ FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_ConstrainedCopy"));
+ }
+
+ HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
+ if (r == AssignDontKnow)
+ {
+ r = CanAssignArrayType(gc.pSrc, gc.pDst);
+ }
+ CONSISTENCY_CHECK(r != AssignDontKnow);
+
+ if (r == AssignWrongType)
+ COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
+
+ // If we were called from Array.ConstrainedCopy, ensure that the array copy
+ // is guaranteed to succeed.
+ _ASSERTE(!reliable || r == AssignWillWork);
+
+ if (m_iLength > 0)
+ {
+ switch (r)
+ {
+ case AssignWillWork:
+ ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+ break;
+
+ case AssignUnboxValueClass:
+ UnBoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+ break;
+
+ case AssignBoxValueClassOrPrimitive:
+ BoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+ break;
+
+ case AssignMustCast:
+ CastCheckEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+ break;
+
+ case AssignPrimitiveWiden:
+ PrimitiveWiden(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+ break;
+
+ default:
+ _ASSERTE(!"Fell through switch in Array.Copy!");
+ }
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+FCIMPL3(void, ArrayNative::ArrayClear, ArrayBase* pArrayUNSAFE, INT32 iIndex, INT32 iLength)
+{
+ FCALL_CONTRACT;
+
+ BASEARRAYREF pArray = (BASEARRAYREF)pArrayUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_1(pArray);
+
+ // cannot pass null for array
+ if (pArray == NULL)
+ COMPlusThrowArgumentNull(W("array"), W("ArgumentNull_Array"));
+
+ // array must be an array
+ _ASSERTE(pArray->GetMethodTable()->IsArray());
+
+ // array bounds checking
+ int lb = pArray->GetLowerBoundsPtr()[0];
+ if (iIndex < lb || (iIndex - lb) < 0 || iLength < 0)
+ COMPlusThrow(kIndexOutOfRangeException);
+
+ if ((iIndex - lb) > (int)pArray->GetNumComponents() - iLength)
+ COMPlusThrow(kIndexOutOfRangeException);
+
+ if (iLength > 0)
+ {
+ char* array = (char*)pArray->GetDataPtr();
+
+ SIZE_T size = pArray->GetComponentSize();
+ _ASSERTE(size >= 1);
+
+ ZeroMemoryInGCHeap(array + (iIndex - lb) * size, iLength * size);
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+
+
+
+
+// Check we're allowed to create an array with the given element type.
+void ArrayNative::CheckElementType(TypeHandle elementType)
+{
+ // Check for simple types first.
+ if (!elementType.IsTypeDesc())
+ {
+ MethodTable *pMT = elementType.AsMethodTable();
+
+ // TODO: We also should check for type/member visibility here. To do that we can replace
+ // the following chunk of code with a simple InvokeUtil::CanAccessClass call.
+ // But it's too late to make this change in Dev10 and we want SL4 to be compatible with Dev10.
+#ifndef FEATURE_CORECLR
+ // Make sure security allows us access to the array type - if it is critical, convert that to a
+ // demand for full trust
+ if (!SecurityStackWalk::HasFlagsOrFullyTrusted(0))
+ {
+ if (Security::TypeRequiresTransparencyCheck(pMT, true))
+ {
+ // If we're creating a critical type, convert the critical check into a demand for full trust
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_FULL_TRUST);
+ }
+ }
+#else
+ if (Security::TypeRequiresTransparencyCheck(pMT))
+ {
+ // The AccessCheckOptions flag doesn't matter because we just need to get the caller.
+ RefSecContext sCtx(AccessCheckOptions::kMemberAccess);
+
+ AccessCheckOptions accessCheckOptions(InvokeUtil::GetInvocationAccessCheckType(),
+ NULL /*pAccessContext*/,
+ TRUE /*throwIfTargetIsInaccessible*/,
+ pMT /*pTargetMT*/);
+
+ accessCheckOptions.DemandMemberAccessOrFail(&sCtx, pMT, FALSE /*visibilityCheck*/);
+ }
+#endif // !FEATURE_CORECLR
+
+ // Check for byref-like types.
+ if (pMT->IsByRefLike())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike[]"));
+
+ // Check for open generic types.
+ if (pMT->IsGenericTypeDefinition() || pMT->ContainsGenericVariables())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_OpenType"));
+
+ // Check for Void.
+ if (elementType.GetSignatureCorElementType() == ELEMENT_TYPE_VOID)
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Void[]"));
+
+ // That's all the dangerous simple types we know, it must be OK.
+ return;
+ }
+
+ // Checks apply recursively for arrays of arrays etc.
+ if (elementType.IsArray())
+ {
+ CheckElementType(elementType.GetElementType());
+ return;
+ }
+
+ // ByRefs and generic type variables are never allowed.
+ if (elementType.IsByRef() || elementType.IsGenericVariable())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+
+ // We can create pointers and function pointers, but it requires skip verification permission.
+ CorElementType etType = elementType.GetSignatureCorElementType();
+ if (etType == ELEMENT_TYPE_PTR || etType == ELEMENT_TYPE_FNPTR)
+ {
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+ return;
+ }
+
+ // We shouldn't get here (it means we've encountered a new type of typehandle if we do).
+ _ASSERTE(!"Shouldn't get here, unknown type handle type");
+ COMPlusThrow(kNotSupportedException);
+}
+
+FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pLowerBounds)
+{
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(rank > 0);
+ PRECONDITION(CheckPointer(pLengths));
+ PRECONDITION(CheckPointer(pLowerBounds, NULL_OK));
+ } CONTRACTL_END;
+
+ OBJECTREF pRet = NULL;
+ TypeHandle elementType = TypeHandle::FromPtr(elementTypeHandle);
+
+ _ASSERTE(!elementType.IsNull());
+
+ // pLengths and pLowerBounds are pinned buffers. No need to protect them.
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ CheckElementType(elementType);
+
+ CorElementType CorType = elementType.GetSignatureCorElementType();
+
+ CorElementType kind = ELEMENT_TYPE_ARRAY;
+
+ // Is it ELEMENT_TYPE_SZARRAY array?
+ if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0)
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us
+ // through the slow path where this will be handled.
+ && (CorType != ELEMENT_TYPE_I8)
+ && (CorType != ELEMENT_TYPE_U8)
+ && (CorType != ELEMENT_TYPE_R8)
+#endif
+ )
+ {
+ // Shortcut for common cases
+ if (CorTypeInfo::IsPrimitiveType(CorType))
+ {
+ pRet = AllocatePrimitiveArray(CorType,pLengths[0]);
+ goto Done;
+ }
+ else
+ if (CorTypeInfo::IsObjRef(CorType))
+ {
+ pRet = AllocateObjectArray(pLengths[0],elementType);
+ goto Done;
+ }
+
+ kind = ELEMENT_TYPE_SZARRAY;
+ pLowerBounds = NULL;
+ }
+
+ {
+ // Find the Array class...
+ TypeHandle typeHnd = ClassLoader::LoadArrayTypeThrowing(elementType, kind, rank);
+
+ DWORD boundsSize = 0;
+ INT32* bounds;
+ if (pLowerBounds != NULL) {
+ if (!ClrSafeInt<DWORD>::multiply(rank, 2, boundsSize))
+ COMPlusThrowOM();
+ DWORD dwAllocaSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize))
+ COMPlusThrowOM();
+
+ bounds = (INT32*) _alloca(dwAllocaSize);
+
+ for (int i=0;i<rank;i++) {
+ bounds[2*i] = pLowerBounds[i];
+ bounds[2*i+1] = pLengths[i];
+ }
+ }
+ else {
+ boundsSize = rank;
+
+ DWORD dwAllocaSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize))
+ COMPlusThrowOM();
+
+ bounds = (INT32*) _alloca(dwAllocaSize);
+
+ // We need to create a private copy of pLengths to avoid holes caused
+ // by caller mutating the array
+ for (int i=0;i<rank;i++)
+ bounds[i] = pLengths[i];
+ }
+
+ pRet = AllocateArrayEx(typeHnd, bounds, boundsSize);
+ }
+
+Done: ;
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(pRet);
+}
+FCIMPLEND
+
+
+FCIMPL4(void, ArrayNative::GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices)
+{
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(rank >= 0);
+ } CONTRACTL_END;
+
+ // FC_GC_POLL not necessary. We poll for GC in Array.Rank that's always called
+ // right before this function
+ FC_GC_POLL_NOT_NEEDED();
+
+ BASEARRAYREF refThis = (BASEARRAYREF) refThisUNSAFE;
+
+ _ASSERTE(rank == (INT32)refThis->GetRank());
+
+ SIZE_T Offset = 0;
+ const INT32 *pBoundsPtr = refThis->GetBoundsPtr();
+
+ if (rank == 1)
+ {
+ Offset = pIndices[0] - refThis->GetLowerBoundsPtr()[0];
+
+ // Bounds check each index
+ // Casting to unsigned allows us to use one compare for [0..limit-1]
+ if (((UINT32) Offset) >= ((UINT32) pBoundsPtr[0]))
+ FCThrowVoid(kIndexOutOfRangeException);
+ }
+ else
+ {
+ // Avoid redundant computation in GetLowerBoundsPtr
+ const INT32 *pLowerBoundsPtr = pBoundsPtr + rank;
+ _ASSERTE(refThis->GetLowerBoundsPtr() == pLowerBoundsPtr);
+
+ SIZE_T Multiplier = 1;
+
+ for (int i = rank; i >= 1; i--) {
+ INT32 curIndex = pIndices[i-1] - pLowerBoundsPtr[i-1];
+
+ // Bounds check each index
+ // Casting to unsigned allows us to use one compare for [0..limit-1]
+ if (((UINT32) curIndex) >= ((UINT32) pBoundsPtr[i-1]))
+ FCThrowVoid(kIndexOutOfRangeException);
+
+ Offset += curIndex * Multiplier;
+ Multiplier *= pBoundsPtr[i-1];
+ }
+ }
+
+ TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle();
+
+ // Legacy behavior
+ if (arrayElementType.IsTypeDesc())
+ {
+ CorElementType elemtype = arrayElementType.AsTypeDesc()->GetInternalCorElementType();
+ if (elemtype == ELEMENT_TYPE_PTR || elemtype == ELEMENT_TYPE_FNPTR)
+ FCThrowResVoid(kNotSupportedException, W("NotSupported_Type"));
+ }
+#ifdef _DEBUG
+ CorElementType elemtype = arrayElementType.GetInternalCorElementType();
+ _ASSERTE(elemtype != ELEMENT_TYPE_PTR && elemtype != ELEMENT_TYPE_FNPTR);
+#endif
+
+ elemRef->data = refThis->GetDataPtr() + (Offset * refThis->GetComponentSize());
+ elemRef->type = arrayElementType;
+}
+FCIMPLEND
+
+FCIMPL2(void, ArrayNative::SetValue, TypedByRef * target, Object* objUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
+
+ TypeHandle thTarget(target->type);
+
+ MethodTable* pTargetMT = thTarget.GetMethodTable();
+ PREFIX_ASSUME(NULL != pTargetMT);
+
+ if (obj == NULL)
+ {
+ // Null is the universal zero...
+ if (pTargetMT->IsValueType())
+ InitValueClass(target->data,pTargetMT);
+ else
+ ClearObjectReference((OBJECTREF*)target->data);
+ }
+ else
+ if (thTarget == TypeHandle(g_pObjectClass))
+ {
+ // Everything is compatible with Object
+ SetObjectReference((OBJECTREF*)target->data,(OBJECTREF)obj,GetAppDomain());
+ }
+ else
+ if (!pTargetMT->IsValueType())
+ {
+ if (ObjIsInstanceOfNoGC(OBJECTREFToObject(obj), thTarget) != TypeHandle::CanCast)
+ {
+ // target->data is protected by the caller
+ HELPER_METHOD_FRAME_BEGIN_1(obj);
+
+ if (!ObjIsInstanceOf(OBJECTREFToObject(obj), thTarget))
+ COMPlusThrow(kInvalidCastException,W("InvalidCast_StoreArrayElement"));
+
+ HELPER_METHOD_FRAME_END();
+ }
+
+ SetObjectReference((OBJECTREF*)target->data,obj,GetAppDomain());
+ }
+ else
+ {
+ // value class or primitive type
+
+ if (!pTargetMT->UnBoxInto(target->data, obj))
+ {
+ // target->data is protected by the caller
+ HELPER_METHOD_FRAME_BEGIN_1(obj);
+
+ ARG_SLOT value = 0;
+
+ // Allow enum -> primitive conversion, disallow primitive -> enum conversion
+ TypeHandle thSrc = obj->GetTypeHandle();
+ CorElementType srcType = thSrc.GetVerifierCorElementType();
+ CorElementType targetType = thTarget.GetSignatureCorElementType();
+
+ if (!InvokeUtil::IsPrimitiveType(srcType) || !InvokeUtil::IsPrimitiveType(targetType))
+ COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement"));
+
+ // Get a properly widened type
+ InvokeUtil::CreatePrimitiveValue(targetType,srcType,obj,&value);
+
+ UINT cbSize = CorTypeInfo::Size(targetType);
+ memcpyNoGCRefs(target->data, ArgSlotEndianessFixup(&value, cbSize), cbSize);
+
+ HELPER_METHOD_FRAME_END();
+ }
+ }
+}
+FCIMPLEND
+
+// This method will initialize an array from a TypeHandle to a field.
+
+FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField)
+{
+ FCALL_CONTRACT;
+
+ BASEARRAYREF arr = BASEARRAYREF(pArrayRef);
+ REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
+ HELPER_METHOD_FRAME_BEGIN_2(arr, refField);
+
+ if ((arr == 0) || (refField == NULL))
+ COMPlusThrow(kArgumentNullException);
+
+ FieldDesc* pField = (FieldDesc*) refField->GetField();
+
+ if (!pField->IsRVA())
+ COMPlusThrow(kArgumentException);
+
+ // Report the RVA field to the logger.
+ g_IBCLogger.LogRVADataAccess(pField);
+
+ // Note that we do not check that the field is actually in the PE file that is initializing
+ // the array. Basically the data being published is can be accessed by anyone with the proper
+ // permissions (C# marks these as assembly visibility, and thus are protected from outside
+ // snooping)
+
+ if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum())
+ COMPlusThrow(kArgumentException);
+
+ SIZE_T dwCompSize = arr->GetComponentSize();
+ SIZE_T dwElemCnt = arr->GetNumComponents();
+ SIZE_T dwTotalSize = dwCompSize * dwElemCnt;
+
+ DWORD size = pField->LoadSize();
+
+ // make certain you don't go off the end of the rva static
+ if (dwTotalSize > size)
+ COMPlusThrow(kArgumentException);
+
+ void *src = pField->GetStaticAddressHandle(NULL);
+ void *dest = arr->GetDataPtr();
+
+#if BIGENDIAN
+ DWORD i;
+ switch (dwCompSize) {
+ case 1:
+ memcpyNoGCRefs(dest, src, dwElemCnt);
+ break;
+ case 2:
+ for (i = 0; i < dwElemCnt; i++)
+ *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i);
+ break;
+ case 4:
+ for (i = 0; i < dwElemCnt; i++)
+ *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i);
+ break;
+ case 8:
+ for (i = 0; i < dwElemCnt; i++)
+ *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i);
+ break;
+ default:
+ // should not reach here.
+ UNREACHABLE_MSG("Incorrect primitive type size!");
+ break;
+ }
+#else
+ memcpyNoGCRefs(dest, src, dwTotalSize);
+#endif
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
diff --git a/src/classlibnative/bcltype/arraynative.h b/src/classlibnative/bcltype/arraynative.h
new file mode 100644
index 0000000000..c742bc21a7
--- /dev/null
+++ b/src/classlibnative/bcltype/arraynative.h
@@ -0,0 +1,81 @@
+// 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.
+//
+// File: ArrayNative.h
+//
+
+//
+// ArrayNative
+// This file defines the native methods for the Array
+//
+
+
+#ifndef _ARRAYNATIVE_H_
+#define _ARRAYNATIVE_H_
+
+#include "fcall.h"
+
+struct FCALLRuntimeFieldHandle
+{
+ ReflectFieldObject *pFieldDONOTUSEDIRECTLY;
+};
+#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY
+
+class ArrayNative
+{
+public:
+ static FCDECL1(INT32, GetRank, ArrayBase* pArray);
+ static FCDECL1(INT32, GetDataPtrOffsetInternal, ArrayBase* array);
+ static FCDECL2(INT32, GetLowerBound, ArrayBase* pArray, unsigned int dimension);
+ static FCDECL2(INT32, GetUpperBound, ArrayBase* pArray, unsigned int dimension);
+ static FCDECL1(INT32, GetLengthNoRank, ArrayBase* pArray);
+ static FCDECL1(INT64, GetLongLengthNoRank, ArrayBase* pArray);
+ static FCDECL2(INT32, GetLength, ArrayBase* pArray, unsigned int dimension);
+ static FCDECL1(void, Initialize, ArrayBase* pArray);
+
+ static FCDECL6(void, ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable);
+ static FCDECL3(void, ArrayClear, ArrayBase* pArrayUNSAFE, INT32 iIndex, INT32 iLength);
+
+ // This method will create a new array of type type, with zero lower
+ // bounds and rank.
+ static FCDECL4(Object*, CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pBounds);
+
+ // This method will return a TypedReference to the array element
+ static FCDECL4(void, GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices);
+
+ // This set of methods will set a value in an array
+ static FCDECL2(void, SetValue, TypedByRef* target, Object* objUNSAFE);
+
+ // This method will initialize an array from a TypeHandle
+ // to a field.
+ static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField);
+
+private:
+ // Helper for CreateInstance
+ static void CheckElementType(TypeHandle elementType);
+
+ // Return values for CanAssignArrayType
+ enum AssignArrayEnum
+ {
+ AssignWrongType,
+ AssignWillWork,
+ AssignMustCast,
+ AssignBoxValueClassOrPrimitive,
+ AssignUnboxValueClass,
+ AssignPrimitiveWiden,
+ AssignDontKnow,
+ };
+
+ // The following functions are all helpers for ArrayCopy
+ static AssignArrayEnum CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
+ static AssignArrayEnum CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
+ static void ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
+ static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
+ static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
+ static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
+ static void PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
+
+};
+
+#endif // _ARRAYNATIVE_H_
diff --git a/src/classlibnative/bcltype/console.cpp b/src/classlibnative/bcltype/console.cpp
new file mode 100644
index 0000000000..7dfb30a5f5
--- /dev/null
+++ b/src/classlibnative/bcltype/console.cpp
@@ -0,0 +1,152 @@
+// 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.
+//
+// File: Console.cpp
+//
+
+//
+// Purpose: Native methods on System.Console
+//
+//
+
+#ifndef FEATURE_CORECLR
+
+#include "common.h"
+#include "sbuffer.h"
+#include <windows.h>
+#include "console.h"
+
+// GetConsoleTitle sometimes interprets the second parameter (nSize) as number of bytes and sometimes as the number of chars.
+// Instead of doing complicated and dangerous logic to determine if this may or may not occur,
+// we simply assume the worst and reserve a bigger buffer. This way we may use a bit more memory,
+// but we will always be safe. This macro helps us doing that:
+#define ADJUST_NUM_CHARS(numChars) ((numChars) * 2)
+
+#define BUFF_SIZE(numChars) ( ((numChars) + 1) * sizeof(TCHAR) )
+
+// A buffer of size ConsoleNative::MaxConsoleTitleLength is quite big.
+// First, we try allocating a smaller buffer because most often, the console title is short.
+// If it turns out that the short buffer size is insufficient, we try again using a larger buffer.
+INT32 QCALLTYPE ConsoleNative::GetTitle(QCall::StringHandleOnStack outTitle, INT32& outTitleLen) {
+
+ QCALL_CONTRACT;
+
+ INT32 result = 0;
+
+ BEGIN_QCALL;
+
+ // Reserve buffer:
+ InlineSBuffer< ADJUST_NUM_CHARS(BUFF_SIZE(ShortConsoleTitleLength)) > titleBuff;
+
+ // Hold last error:
+ DWORD lastError;
+
+ // Read console title, get length of the title:
+
+ BYTE *buffPtr = titleBuff.OpenRawBuffer( ADJUST_NUM_CHARS(BUFF_SIZE(ShortConsoleTitleLength)) );
+
+ SetLastError(0);
+ DWORD len = GetConsoleTitle((TCHAR *) buffPtr, ADJUST_NUM_CHARS(ShortConsoleTitleLength + 1));
+ lastError = GetLastError();
+
+ titleBuff.CloseRawBuffer();
+
+ // If the title length is larger than supported maximum, do not bother reading it, just return the length:
+ if (len > MaxConsoleTitleLength) {
+
+ outTitleLen = len;
+ outTitle.Set(W(""));
+ result = 0;
+
+ // If title length is within valid range:
+ } else {
+
+ // If the title is longer than the short buffer, but can fit in the max supported length,
+ // read it again with the long buffer:
+ if (len > ShortConsoleTitleLength) {
+
+ COUNT_T buffSize = ADJUST_NUM_CHARS(BUFF_SIZE(len));
+ titleBuff.SetSize(buffSize);
+
+ BYTE *buffPtr = titleBuff.OpenRawBuffer(buffSize);
+
+ SetLastError(0);
+ len = GetConsoleTitle((TCHAR *) buffPtr, ADJUST_NUM_CHARS(len + 1));
+ lastError = GetLastError();
+
+ titleBuff.CloseRawBuffer();
+ }
+
+ // Zero may indicate error or empty title. Check for error:
+ result = (INT32) (0 == len ? lastError : 0);
+
+ // If no error, set title and length:
+ if (0 == result) {
+ const BYTE *cBuffPtr = (const BYTE *) titleBuff;
+ outTitle.Set((TCHAR *) cBuffPtr);
+ outTitleLen = (INT32) len;
+
+ // If error, set to empty:
+ } else {
+ outTitleLen = (INT32) -1;
+ // No need to set the title string if we have an error anyway.
+ }
+ } // if title length is within valid range.
+
+ END_QCALL;
+
+ return result;
+}
+
+// Block waiting for data to become available on the console stream indicated by the safe file handle passed.
+// Ensure that the thread remains abortable in the process.
+FCIMPL2(void, ConsoleStreamHelper::WaitForAvailableConsoleInput, SafeHandle* refThisUNSAFE, CLR_BOOL bIsPipe)
+{
+ FCALL_CONTRACT;
+
+ SAFEHANDLEREF refConsoleHandle(refThisUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_1(refConsoleHandle);
+
+ // Prevent the console handle being closed under our feet.
+ SafeHandleHolder shh(&refConsoleHandle);
+
+ // Don't pass the address of the native handle within the safe handle to DoAppropriateWait since the safe
+ // handle is on the GC heap and could be moved. Instead copy the native handle out into a stack location
+ // (this is safe because we've ref-counted the safe handle to prevent it being disposed on us).
+ HANDLE hNativeConsoleHandle = refConsoleHandle->GetHandle();
+
+ bool skipWait = false;
+
+ // If we are reading from a pipe and the other end of the pipe was closed, then do not block. No one can write to it.
+ // Also we can skip blocking if we do have data available. We should block if nothing is available, with the assumption
+ // that Windows is smart enough to handle pipes where the other end is closed.
+ if (bIsPipe)
+ {
+ DWORD cBytesRead, cTotalBytesAvailable, cBytesLeftThisMessage;
+ int r = PeekNamedPipe(hNativeConsoleHandle, NULL, 0, &cBytesRead, &cTotalBytesAvailable, &cBytesLeftThisMessage);
+ if (r != 0)
+ {
+ skipWait = cTotalBytesAvailable > 0;
+ }
+ else
+ {
+ // Windows returns ERROR_BROKEN_PIPE if the other side of a pipe is closed. However, we've seen
+ // pipes return ERROR_NO_DATA and ERROR_PIPE_NOT_CONNECTED. Check for those too.
+ int errorCode = GetLastError();
+ skipWait = errorCode == ERROR_BROKEN_PIPE || errorCode == ERROR_NO_DATA || errorCode == ERROR_PIPE_NOT_CONNECTED;
+ }
+ }
+
+ // Perform the wait (DoAppropriateWait automatically handles thread aborts).
+ if (!skipWait)
+ {
+ GetThread()->DoAppropriateWait(1, &hNativeConsoleHandle, TRUE, INFINITE, WaitMode_Alertable);
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // ifndef FEATURE_CORECLR
diff --git a/src/classlibnative/bcltype/console.h b/src/classlibnative/bcltype/console.h
new file mode 100644
index 0000000000..b3772f2425
--- /dev/null
+++ b/src/classlibnative/bcltype/console.h
@@ -0,0 +1,45 @@
+// 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.
+//
+// File: Console.h
+//
+
+//
+// Purpose: Native methods on System.Console
+//
+
+//
+#ifndef _CONSOLE_H_
+#define _CONSOLE_H_
+
+#ifndef FEATURE_CORECLR
+
+#include "qcall.h"
+
+class ConsoleNative {
+
+private:
+
+ // Short buffer len to try using first:
+ static const INT32 ShortConsoleTitleLength = 200;
+
+public:
+
+ // This value is copied from Console.cs. There is said:
+ // MSDN says console titles can be up to 64KB in length.
+ // But there is an exception if the buffer lengths longer than
+ // ~24500 Unicode characters are used. Oh well.
+ static const INT32 MaxConsoleTitleLength = 24500;
+
+ static INT32 QCALLTYPE GetTitle(QCall::StringHandleOnStack outTitle, INT32& outTitleLen);
+};
+
+class ConsoleStreamHelper {
+public:
+ static FCDECL2(void, WaitForAvailableConsoleInput, SafeHandle* refThisUNSAFE, CLR_BOOL bIsPipe);
+};
+
+#endif // ifndef FEATURE_CORECLR
+
+#endif // _CONSOLE_H_
diff --git a/src/classlibnative/bcltype/currency.cpp b/src/classlibnative/bcltype/currency.cpp
new file mode 100644
index 0000000000..928a903f3f
--- /dev/null
+++ b/src/classlibnative/bcltype/currency.cpp
@@ -0,0 +1,42 @@
+// 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.
+//
+// File: Currency.cpp
+//
+
+//
+
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "currency.h"
+#include "string.h"
+
+
+FCIMPL2(void, COMCurrency::DoToDecimal, DECIMAL * result, CY c)
+{
+ FCALL_CONTRACT;
+
+ // GC could only happen when exception is thrown, no need to protect result
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ _ASSERTE(result);
+ HRESULT hr = VarDecFromCy(c, result);
+ if (FAILED(hr))
+ {
+ // Didn't expect to get here. Update code for this HR.
+ _ASSERTE(S_OK == hr);
+ COMPlusThrowHR(hr);
+ }
+
+ if (FAILED(DecimalCanonicalize(result)))
+ COMPlusThrow(kOverflowException, W("Overflow_Currency"));
+
+ result->wReserved = 0;
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
diff --git a/src/classlibnative/bcltype/currency.h b/src/classlibnative/bcltype/currency.h
new file mode 100644
index 0000000000..00c3e8dd5c
--- /dev/null
+++ b/src/classlibnative/bcltype/currency.h
@@ -0,0 +1,24 @@
+// 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.
+//
+// File: Currency.h
+//
+
+//
+
+#ifndef _CURRENCY_H_
+#define _CURRENCY_H_
+
+#include <oleauto.h>
+#include <pshpack1.h>
+
+class COMCurrency
+{
+public:
+ static FCDECL2 (void, DoToDecimal, DECIMAL * result, CY c);
+};
+
+#include <poppack.h>
+
+#endif // _CURRENCY_H_
diff --git a/src/classlibnative/bcltype/decimal.cpp b/src/classlibnative/bcltype/decimal.cpp
new file mode 100644
index 0000000000..ed63391e54
--- /dev/null
+++ b/src/classlibnative/bcltype/decimal.cpp
@@ -0,0 +1,2588 @@
+// 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.
+//
+// File: decimal.cpp
+//
+
+//
+
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "decimal.h"
+#include "string.h"
+
+LONG g_OLEAUT32_Loaded = 0;
+
+unsigned int DecDivMod1E9(DECIMAL* value);
+void DecMul10(DECIMAL* value);
+void DecAddInt32(DECIMAL* value, unsigned int i);
+
+#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); DECIMAL_LO64_SET(dest, DECIMAL_LO64_GET(src));}
+
+FCIMPL2_IV(void, COMDecimal::InitSingle, DECIMAL *_this, float value)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ _ASSERTE(_this != NULL);
+ HRESULT hr = VarDecFromR4(value, _this);
+ if (FAILED(hr))
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ _this->wReserved = 0;
+}
+FCIMPLEND
+
+FCIMPL2_IV(void, COMDecimal::InitDouble, DECIMAL *_this, double value)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ _ASSERTE(_this != NULL);
+ HRESULT hr = VarDecFromR8(value, _this);
+ if (FAILED(hr))
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ _this->wReserved = 0;
+}
+FCIMPLEND
+
+
+#ifdef _MSC_VER
+// C4702: unreachable code on IA64 retail
+#pragma warning(push)
+#pragma warning(disable:4702)
+#endif
+FCIMPL2(INT32, COMDecimal::DoCompare, DECIMAL * d1, DECIMAL * d2)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ HRESULT hr = VarDecCmp(d1, d2);
+ if (FAILED(hr) || (int)hr == VARCMP_NULL) {
+ _ASSERTE(!"VarDecCmp failed in Decimal::Compare");
+ FCThrowRes(kOverflowException, W("Overflow_Decimal"));
+ }
+
+ INT32 retVal = ((int)hr) - 1;
+ FC_GC_POLL_RET ();
+ return retVal;
+}
+FCIMPLEND
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+FCIMPL1(void, COMDecimal::DoFloor, DECIMAL * d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL decRes;
+ HRESULT hr;
+ hr = VarDecInt(d, &decRes);
+
+ // VarDecInt can't overflow, as of source for OleAut32 build 4265.
+ // It only returns NOERROR
+ _ASSERTE(hr==NOERROR);
+
+ // copy decRes into d
+ COPYDEC(*d, decRes)
+ d->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+FCIMPL1(INT32, COMDecimal::GetHashCode, DECIMAL *d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ _ASSERTE(d != NULL);
+ double dbl;
+ VarR8FromDec(d, &dbl);
+ if (dbl == 0.0) {
+ // Ensure 0 and -0 have the same hash code
+ return 0;
+ }
+ // conversion to double is lossy and produces rounding errors so we mask off the lowest 4 bits
+ //
+ // For example these two numerically equal decimals with different internal representations produce
+ // slightly different results when converted to double:
+ //
+ // decimal a = new decimal(new int[] { 0x76969696, 0x2fdd49fa, 0x409783ff, 0x00160000 });
+ // => (decimal)1999021.176470588235294117647000000000 => (double)1999021.176470588
+ // decimal b = new decimal(new int[] { 0x3f0f0f0f, 0x1e62edcc, 0x06758d33, 0x00150000 });
+ // => (decimal)1999021.176470588235294117647000000000 => (double)1999021.1764705882
+ //
+ return ((((int *)&dbl)[0]) & 0xFFFFFFF0) ^ ((int *)&dbl)[1];
+}
+FCIMPLEND
+
+FCIMPL3(void, COMDecimal::DoMultiply, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL decRes;
+
+ // GC is only triggered for throwing, no need to protect result
+ HRESULT hr = VarDecMul(d1, d2, &decRes);
+ if (FAILED(hr)) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+ // copy decRes into d1
+ COPYDEC(*d1, decRes)
+ d1->wReserved = 0;
+ *overflowed = false;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+
+FCIMPL2(void, COMDecimal::DoMultiplyThrow, DECIMAL * d1, DECIMAL * d2)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL decRes;
+
+ // GC is only triggered for throwing, no need to protect result
+ HRESULT hr = VarDecMul(d1, d2, &decRes);
+ if (FAILED(hr)) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+ // copy decRes into d1
+ COPYDEC(*d1, decRes)
+ d1->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+FCIMPL2(void, COMDecimal::DoRound, DECIMAL * d, INT32 decimals)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL decRes;
+
+ // GC is only triggered for throwing, no need to protect result
+ if (decimals < 0 || decimals > 28)
+ FCThrowArgumentOutOfRangeVoid(W("decimals"), W("ArgumentOutOfRange_DecimalRound"));
+ HRESULT hr = VarDecRound(d, decimals, &decRes);
+ if (FAILED(hr))
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+
+ // copy decRes into d
+ COPYDEC(*d, decRes)
+ d->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+FCIMPL2_IV(void, COMDecimal::DoToCurrency, CY * result, DECIMAL d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ // GC is only triggered for throwing, no need to protect result
+ HRESULT hr = VarCyFromDec(&d, result);
+ if (FAILED(hr)) {
+ _ASSERTE(hr != E_INVALIDARG);
+ FCThrowResVoid(kOverflowException, W("Overflow_Currency"));
+ }
+}
+FCIMPLEND
+
+FCIMPL1(double, COMDecimal::ToDouble, FC_DECIMAL d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ double result = 0.0;
+ // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0
+ VarR8FromDec(&d, &result);
+ return result;
+}
+FCIMPLEND
+
+FCIMPL1(INT32, COMDecimal::ToInt32, FC_DECIMAL d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL result;
+ HRESULT hr = VarDecRound(&d, 0, &result);
+ if (FAILED(hr))
+ FCThrowRes(kOverflowException, W("Overflow_Decimal"));
+
+ result.wReserved = 0;
+
+ if( DECIMAL_SCALE(result) != 0) {
+ d = result;
+ VarDecFix(&d, &result);
+ }
+
+ if (DECIMAL_HI32(result) == 0 && DECIMAL_MID32(result) == 0) {
+ INT32 i = DECIMAL_LO32(result);
+ if ((INT16)DECIMAL_SIGNSCALE(result) >= 0) {
+ if (i >= 0) return i;
+ }
+ else {
+ // Int32.MinValue is represented as sign being negative
+ // and Lo32 being 0x80000000 (-ve number). Return that as is without
+ // reversing the sign of the number.
+ if(i == 0x80000000) return i;
+ i = -i;
+ if (i <= 0) return i;
+ }
+ }
+ FCThrowRes(kOverflowException, W("Overflow_Int32"));
+}
+FCIMPLEND
+
+FCIMPL1(float, COMDecimal::ToSingle, FC_DECIMAL d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ float result = 0.0f;
+ // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0
+ VarR4FromDec(&d, &result);
+ return result;
+}
+FCIMPLEND
+
+FCIMPL1(void, COMDecimal::DoTruncate, DECIMAL * d)
+{
+ FCALL_CONTRACT;
+
+ ENSURE_OLEAUT32_LOADED();
+
+ DECIMAL decRes;
+
+ VarDecFix(d, &decRes);
+
+ // copy decRes into d
+ COPYDEC(*d, decRes)
+ d->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+
+void COMDecimal::DecimalToNumber(DECIMAL* value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(value != NULL);
+
+ wchar_t buffer[DECIMAL_PRECISION+1];
+ DECIMAL d = *value;
+ number->precision = DECIMAL_PRECISION;
+ number->sign = DECIMAL_SIGN(d)? 1: 0;
+ wchar_t* p = buffer + DECIMAL_PRECISION;
+ while (DECIMAL_MID32(d) | DECIMAL_HI32(d)) {
+ p = COMNumber::Int32ToDecChars(p, DecDivMod1E9(&d), 9);
+ _ASSERTE(p != NULL);
+ }
+ p = COMNumber::Int32ToDecChars(p, DECIMAL_LO32(d), 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + DECIMAL_PRECISION - p);
+ number->scale = i - DECIMAL_SCALE(d);
+ wchar_t* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+}
+
+int COMDecimal::NumberToDecimal(NUMBER* number, DECIMAL* value)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(value != NULL);
+
+ DECIMAL d;
+ d.wReserved = 0;
+ DECIMAL_SIGNSCALE(d) = 0;
+ DECIMAL_HI32(d) = 0;
+ DECIMAL_LO32(d) = 0;
+ DECIMAL_MID32(d) = 0;
+ wchar_t* p = number->digits;
+ _ASSERT(p != NULL);
+ int e = number->scale;
+ if (!*p) {
+ // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
+ // the scale to 0 if the scale was previously positive
+ if (e > 0) {
+ e = 0;
+ }
+ } else {
+ if (e > DECIMAL_PRECISION) return 0;
+ while ((e > 0 || (*p && e > -28)) &&
+ (DECIMAL_HI32(d) < 0x19999999 || (DECIMAL_HI32(d) == 0x19999999 &&
+ (DECIMAL_MID32(d) < 0x99999999 || (DECIMAL_MID32(d) == 0x99999999 &&
+ (DECIMAL_LO32(d) < 0x99999999 || (DECIMAL_LO32(d) == 0x99999999 && *p <= '5'))))))) {
+ DecMul10(&d);
+ if (*p) DecAddInt32(&d, *p++ - '0');
+ e--;
+ }
+ if (*p++ >= '5') {
+ bool round = true;
+ if (*(p-1) == '5' && *(p-2) % 2 == 0) { // Check if previous digit is even, only if the when we are unsure whether hows to do Banker's rounding
+ // For digits > 5 we will be roundinp up anyway.
+ int count = 20; // Look at the next 20 digits to check to round
+ while (*p == '0' && count != 0) {
+ p++;
+ count--;
+ }
+ if (*p == '\0' || count == 0)
+ round = false;// Do nothing
+ }
+
+ if (round) {
+ DecAddInt32(&d, 1);
+ if ((DECIMAL_HI32(d) | DECIMAL_MID32(d) | DECIMAL_LO32(d)) == 0) {
+ DECIMAL_HI32(d) = 0x19999999;
+ DECIMAL_MID32(d) = 0x99999999;
+ DECIMAL_LO32(d) = 0x9999999A;
+ e++;
+ }
+ }
+ }
+ }
+ if (e > 0) return 0;
+ if (e <= -DECIMAL_PRECISION)
+ {
+ // Parsing a large scale zero can give you more precision than fits in the decimal.
+ // This should only happen for actual zeros or very small numbers that round to zero.
+ DECIMAL_SIGNSCALE(d) = 0;
+ DECIMAL_HI32(d) = 0;
+ DECIMAL_LO32(d) = 0;
+ DECIMAL_MID32(d) = 0;
+ DECIMAL_SCALE(d) = (DECIMAL_PRECISION - 1);
+ }
+ else
+ {
+ DECIMAL_SCALE(d) = static_cast<BYTE>(-e);
+ }
+ DECIMAL_SIGN(d) = number->sign? DECIMAL_NEG: 0;
+ *value = d;
+ return 1;
+}
+
+#if defined(_TARGET_X86_)
+
+#pragma warning(disable:4035)
+
+unsigned int DecDivMod1E9(DECIMAL* value)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov ebx,value
+ mov ecx,1000000000
+ xor edx,edx
+ mov eax,[ebx+4]
+ div ecx
+ mov [ebx+4],eax
+ mov eax,[ebx+12]
+ div ecx
+ mov [ebx+12],eax
+ mov eax,[ebx+8]
+ div ecx
+ mov [ebx+8],eax
+ mov eax,edx
+ }
+}
+
+void DecMul10(DECIMAL* value)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov ebx,value
+ mov eax,[ebx+8]
+ mov edx,[ebx+12]
+ mov ecx,[ebx+4]
+ shl eax,1
+ rcl edx,1
+ rcl ecx,1
+ shl eax,1
+ rcl edx,1
+ rcl ecx,1
+ add eax,[ebx+8]
+ adc edx,[ebx+12]
+ adc ecx,[ebx+4]
+ shl eax,1
+ rcl edx,1
+ rcl ecx,1
+ mov [ebx+8],eax
+ mov [ebx+12],edx
+ mov [ebx+4],ecx
+ }
+}
+
+void DecAddInt32(DECIMAL* value, unsigned int i)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov edx,value
+ mov eax,i
+ add dword ptr [edx+8],eax
+ adc dword ptr [edx+12],0
+ adc dword ptr [edx+4],0
+ }
+}
+
+#pragma warning(default:4035)
+
+#else // !(defined(_TARGET_X86_)
+
+unsigned int D32DivMod1E9(unsigned int hi32, ULONG* lo32)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(lo32 != NULL);
+
+ unsigned __int64 n = (unsigned __int64)hi32 << 32 | *lo32;
+ *lo32 = (unsigned int)(n / 1000000000);
+ return (unsigned int)(n % 1000000000);
+}
+
+unsigned int DecDivMod1E9(DECIMAL* value)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(value != NULL);
+
+ return D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0,
+ &DECIMAL_HI32(*value)), &DECIMAL_MID32(*value)), &DECIMAL_LO32(*value));
+}
+
+void DecShiftLeft(DECIMAL* value)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(value != NULL);
+
+ unsigned int c0 = DECIMAL_LO32(*value) & 0x80000000? 1: 0;
+ unsigned int c1 = DECIMAL_MID32(*value) & 0x80000000? 1: 0;
+ DECIMAL_LO32(*value) <<= 1;
+ DECIMAL_MID32(*value) = DECIMAL_MID32(*value) << 1 | c0;
+ DECIMAL_HI32(*value) = DECIMAL_HI32(*value) << 1 | c1;
+}
+
+int D32AddCarry(ULONG* value, unsigned int i)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(value != NULL);
+
+ unsigned int v = *value;
+ unsigned int sum = v + i;
+ *value = sum;
+ return sum < v || sum < i? 1: 0;
+}
+
+void DecAdd(DECIMAL* value, DECIMAL* d)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(value != NULL && d != NULL);
+
+ if (D32AddCarry(&DECIMAL_LO32(*value), DECIMAL_LO32(*d))) {
+ if (D32AddCarry(&DECIMAL_MID32(*value), 1)) {
+ D32AddCarry(&DECIMAL_HI32(*value), 1);
+ }
+ }
+ if (D32AddCarry(&DECIMAL_MID32(*value), DECIMAL_MID32(*d))) {
+ D32AddCarry(&DECIMAL_HI32(*value), 1);
+ }
+ D32AddCarry(&DECIMAL_HI32(*value), DECIMAL_HI32(*d));
+}
+
+void DecMul10(DECIMAL* value)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(value != NULL);
+
+ DECIMAL d = *value;
+ DecShiftLeft(value);
+ DecShiftLeft(value);
+ DecAdd(value, &d);
+ DecShiftLeft(value);
+}
+
+void DecAddInt32(DECIMAL* value, unsigned int i)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(value != NULL);
+
+ if (D32AddCarry(&DECIMAL_LO32(*value), i)) {
+ if (D32AddCarry(&DECIMAL_MID32(*value), 1)) {
+ D32AddCarry(&DECIMAL_HI32(*value), 1);
+ }
+ }
+}
+
+#endif
+
+/***
+*
+* Decimal Code ported from OleAut32
+*
+***********************************************************************/
+
+// This OleAut code is only used on 64-bit and rotor platforms. It is desiriable to continue
+// to call the OleAut routines in X86 because of the performance of the hand-tuned assembly
+// code and because there are currently no inconsistencies in behavior accross platforms.
+
+#ifndef UInt32x32To64
+#define UInt32x32To64(a, b) ((DWORDLONG)((DWORD)(a)) * (DWORDLONG)((DWORD)(b)))
+#endif
+
+typedef union {
+ DWORDLONG int64;
+ struct {
+#if BIGENDIAN
+ ULONG Hi;
+ ULONG Lo;
+#else
+ ULONG Lo;
+ ULONG Hi;
+#endif
+ } u;
+} SPLIT64;
+
+#define OVFL_MAX_1_HI 429496729
+#define DEC_SCALE_MAX 28
+#define POWER10_MAX 9
+
+#define OVFL_MAX_9_HI 4u
+#define OVFL_MAX_9_MID 1266874889u
+#define OVFL_MAX_9_LO 3047500985u
+
+#define OVFL_MAX_5_HI 42949
+
+
+const ULONG rgulPower10[POWER10_MAX+1] = {1, 10, 100, 1000, 10000, 100000, 1000000,
+ 10000000, 100000000, 1000000000};
+
+struct DECOVFL
+{
+ ULONG Hi;
+ ULONG Mid;
+ ULONG Lo;
+};
+
+const DECOVFL PowerOvfl[] = {
+// 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.
+//
+ { 429496729u, 2576980377u, 2576980377u }, // 10^1 remainder 0.6
+ { 42949672u, 4123168604u, 687194767u }, // 10^2 remainder 0.16
+ { 4294967u, 1271310319u, 2645699854u }, // 10^3 remainder 0.616
+ { 429496u, 3133608139u, 694066715u }, // 10^4 remainder 0.1616
+ { 42949u, 2890341191u, 2216890319u }, // 10^5 remainder 0.51616
+ { 4294u, 4154504685u, 2369172679u }, // 10^6 remainder 0.551616
+ { 429u, 2133437386u, 4102387834u }, // 10^7 remainder 0.9551616
+ { 42u, 4078814305u, 410238783u }, // 10^8 remainder 0.09991616
+ { 4u, 1266874889u, 3047500985u }, // 10^9 remainder 0.709551616
+};
+
+
+/***
+* IncreaseScale
+*
+* Entry:
+* rgulNum - Pointer to 96-bit number as array of ULONGs, least-sig first
+* ulPwr - Scale factor to multiply by
+*
+* Purpose:
+* Multiply the two numbers. The low 96 bits of the result overwrite
+* the input. The last 32 bits of the product are the return value.
+*
+* Exit:
+* Returns highest 32 bits of product.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG IncreaseScale(ULONG *rgulNum, ULONG ulPwr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ SPLIT64 sdlTmp;
+
+ sdlTmp.int64 = UInt32x32To64(rgulNum[0], ulPwr);
+ rgulNum[0] = sdlTmp.u.Lo;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[1], ulPwr) + sdlTmp.u.Hi;
+ rgulNum[1] = sdlTmp.u.Lo;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[2], ulPwr) + sdlTmp.u.Hi;
+ rgulNum[2] = sdlTmp.u.Lo;
+ return sdlTmp.u.Hi;
+}
+
+
+/***
+* SearchScale
+*
+* Entry:
+* ulResHi - Top ULONG of quotient
+* ulResMid - Middle ULONG of quotient
+* ulResLo - Bottom 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, -1 if overflow error.
+*
+***********************************************************************/
+
+int SearchScale(ULONG ulResHi, ULONG ulResMid, ULONG ulResLo, int iScale)
+{
+ WRAPPER_NO_CONTRACT;
+
+ int iCurScale;
+
+ // Quick check to stop us from trying to scale any more.
+ //
+ if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX) {
+ iCurScale = 0;
+ goto HaveScale;
+ }
+
+ if (iScale > DEC_SCALE_MAX - 9) {
+ // We can't scale by 10^9 without exceeding the max scale factor.
+ // See if we can scale to the max. If not, we'll fall into
+ // standard search for scale factor.
+ //
+ iCurScale = DEC_SCALE_MAX - iScale;
+ if (ulResHi < PowerOvfl[iCurScale - 1].Hi)
+ goto HaveScale;
+
+ if (ulResHi == PowerOvfl[iCurScale - 1].Hi) {
+ UpperEq:
+ if (ulResMid > PowerOvfl[iCurScale - 1].Mid ||
+ (ulResMid == PowerOvfl[iCurScale - 1].Mid && ulResLo > PowerOvfl[iCurScale - 1].Lo)) {
+ iCurScale--;
+ }
+ goto HaveScale;
+ }
+ }
+ 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
+ // on PowerOvfl[].
+ //
+ iCurScale = 5;
+ if (ulResHi < OVFL_MAX_5_HI)
+ iCurScale = 7;
+ else if (ulResHi > OVFL_MAX_5_HI)
+ iCurScale = 3;
+ else
+ goto UpperEq;
+
+ // iCurScale is 3 or 7.
+ //
+ if (ulResHi < PowerOvfl[iCurScale - 1].Hi)
+ iCurScale++;
+ else if (ulResHi > PowerOvfl[iCurScale - 1].Hi)
+ iCurScale--;
+ else
+ goto UpperEq;
+
+ // 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)
+ iCurScale--;
+
+ if (ulResHi == PowerOvfl[iCurScale - 1].Hi)
+ goto UpperEq;
+
+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)
+ iCurScale = -1;
+
+ return iCurScale;
+}
+
+//***********************************************************************
+//
+// Arithmetic Inlines
+//
+
+#define Div64by32(num, den) ((ULONG)((DWORDLONG)(num) / (ULONG)(den)))
+#define Mod64by32(num, den) ((ULONG)((DWORDLONG)(num) % (ULONG)(den)))
+
+inline DWORDLONG DivMod64by32(DWORDLONG num, ULONG den)
+{
+ WRAPPER_NO_CONTRACT;
+
+ SPLIT64 sdl;
+
+ sdl.u.Lo = Div64by32(num, den);
+ sdl.u.Hi = Mod64by32(num, den);
+ return sdl.int64;
+}
+
+/***
+* Div128By96
+*
+* Entry:
+* 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.
+* Top divisor ULONG must be larger than top dividend ULONG. This is
+* assured in the initial call because the divisor is normalized
+* and the dividend can't be. In subsequent calls, the remainder
+* is multiplied by 10^9 (max), so it can be no more than 1/4 of
+* the divisor which is effectively multiplied by 2^32 (4 * 10^9).
+*
+* Exit:
+* Remainder overwrites lower 96-bits of dividend.
+* Returns quotient.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG Div128By96(ULONG *rgulNum, ULONG *rgulDen)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ SPLIT64 sdlQuo;
+ SPLIT64 sdlNum;
+ SPLIT64 sdlProd1;
+ SPLIT64 sdlProd2;
+
+ sdlNum.u.Lo = rgulNum[0];
+ sdlNum.u.Hi = rgulNum[1];
+
+ if (rgulNum[3] == 0 && rgulNum[2] < rgulDen[2])
+ // Result is zero. Entire dividend is remainder.
+ //
+ return 0;
+
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlQuo.u.Lo = rgulNum[2];
+ sdlQuo.u.Hi = rgulNum[3];
+ sdlQuo.int64 = DivMod64by32(sdlQuo.int64, rgulDen[2]);
+
+ // Compute full remainder, rem = dividend - (quo * divisor).
+ //
+ sdlProd1.int64 = UInt32x32To64(sdlQuo.u.Lo, rgulDen[0]); // quo * lo divisor
+ sdlProd2.int64 = UInt32x32To64(sdlQuo.u.Lo, rgulDen[1]); // quo * mid divisor
+ sdlProd2.int64 += sdlProd1.u.Hi;
+ sdlProd1.u.Hi = sdlProd2.u.Lo;
+
+ sdlNum.int64 -= sdlProd1.int64;
+ rgulNum[2] = sdlQuo.u.Hi - sdlProd2.u.Hi; // sdlQuo.Hi is remainder
+
+ // Propagate carries
+ //
+ if (sdlNum.int64 > ~sdlProd1.int64) {
+ rgulNum[2]--;
+ if (rgulNum[2] >= ~sdlProd2.u.Hi)
+ goto NegRem;
+ }
+ else if (rgulNum[2] > ~sdlProd2.u.Hi) {
+NegRem:
+ // Remainder went negative. Add divisor back in until it's positive,
+ // a max of 2 times.
+ //
+ sdlProd1.u.Lo = rgulDen[0];
+ sdlProd1.u.Hi = rgulDen[1];
+
+ for (;;) {
+ sdlQuo.u.Lo--;
+ sdlNum.int64 += sdlProd1.int64;
+ rgulNum[2] += rgulDen[2];
+
+ if (sdlNum.int64 < sdlProd1.int64) {
+ // Detected carry. Check for carry out of top
+ // before adding it in.
+ //
+ if (rgulNum[2]++ < rgulDen[2])
+ break;
+ }
+ if (rgulNum[2] < rgulDen[2])
+ break; // detected carry
+ }
+ }
+
+ rgulNum[0] = sdlNum.u.Lo;
+ rgulNum[1] = sdlNum.u.Hi;
+ return sdlQuo.u.Lo;
+}
+
+
+
+/***
+* Div96By32
+*
+* Entry:
+* rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
+* ulDen - 32-bit divisor.
+*
+* Purpose:
+* Do full divide, yielding 96-bit result and 32-bit remainder.
+*
+* Exit:
+* Quotient overwrites dividend.
+* Returns remainder.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG Div96By32(ULONG *rgulNum, ULONG ulDen)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ SPLIT64 sdlTmp;
+
+ sdlTmp.u.Hi = 0;
+
+ if (rgulNum[2] != 0)
+ goto Div3Word;
+
+ if (rgulNum[1] >= ulDen)
+ goto Div2Word;
+
+ sdlTmp.u.Hi = rgulNum[1];
+ rgulNum[1] = 0;
+ goto Div1Word;
+
+Div3Word:
+ sdlTmp.u.Lo = rgulNum[2];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[2] = sdlTmp.u.Lo;
+Div2Word:
+ sdlTmp.u.Lo = rgulNum[1];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[1] = sdlTmp.u.Lo;
+Div1Word:
+ sdlTmp.u.Lo = rgulNum[0];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[0] = sdlTmp.u.Lo;
+ return sdlTmp.u.Hi;
+}
+
+
+/***
+* Div96By64
+*
+* Entry:
+* rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
+* sdlDen - 64-bit divisor.
+*
+* Purpose:
+* Do partial divide, yielding 32-bit result and 64-bit remainder.
+* Divisor must be larger than upper 64 bits of dividend.
+*
+* Exit:
+* Remainder overwrites lower 64-bits of dividend.
+* Returns quotient.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG Div96By64(ULONG *rgulNum, SPLIT64 sdlDen)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ SPLIT64 sdlQuo;
+ SPLIT64 sdlNum;
+ SPLIT64 sdlProd;
+
+ sdlNum.u.Lo = rgulNum[0];
+
+ if (rgulNum[2] >= sdlDen.u.Hi) {
+ // Divide would overflow. Assume a quotient of 2^32, and set
+ // up remainder accordingly. Then jump to loop which reduces
+ // the quotient.
+ //
+ sdlNum.u.Hi = rgulNum[1] - sdlDen.u.Lo;
+ sdlQuo.u.Lo = 0;
+ goto NegRem;
+ }
+
+ // Hardware divide won't overflow
+ //
+ if (rgulNum[2] == 0 && rgulNum[1] < sdlDen.u.Hi)
+ // Result is zero. Entire dividend is remainder.
+ //
+ return 0;
+
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlQuo.u.Lo = rgulNum[1];
+ sdlQuo.u.Hi = rgulNum[2];
+ sdlQuo.int64 = DivMod64by32(sdlQuo.int64, sdlDen.u.Hi);
+ sdlNum.u.Hi = sdlQuo.u.Hi; // remainder
+
+ // Compute full remainder, rem = dividend - (quo * divisor).
+ //
+ sdlProd.int64 = UInt32x32To64(sdlQuo.u.Lo, sdlDen.u.Lo); // quo * lo divisor
+ sdlNum.int64 -= sdlProd.int64;
+
+ if (sdlNum.int64 > ~sdlProd.int64) {
+NegRem:
+ // Remainder went negative. Add divisor back in until it's positive,
+ // a max of 2 times.
+ //
+ do {
+ sdlQuo.u.Lo--;
+ sdlNum.int64 += sdlDen.int64;
+ }while (sdlNum.int64 >= sdlDen.int64);
+ }
+
+ rgulNum[0] = sdlNum.u.Lo;
+ rgulNum[1] = sdlNum.u.Hi;
+ return sdlQuo.u.Lo;
+}
+
+// Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer
+// Returns FALSE if there is an overflow
+BOOL Add32To96(ULONG *rgulNum, ULONG ulValue) {
+ rgulNum[0] += ulValue;
+ if (rgulNum[0] < ulValue) {
+ if (++rgulNum[1] == 0) {
+ if (++rgulNum[2] == 0) {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// 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,
+void OverflowUnscale(ULONG *rgulQuo, BOOL fRemainder) {
+ LIMITED_METHOD_CONTRACT;
+
+ SPLIT64 sdlTmp;
+
+ // We have overflown, so load the high bit with a one.
+ sdlTmp.u.Hi = 1u;
+ sdlTmp.u.Lo = rgulQuo[2];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
+ rgulQuo[2] = sdlTmp.u.Lo;
+ sdlTmp.u.Lo = rgulQuo[1];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
+ rgulQuo[1] = sdlTmp.u.Lo;
+ sdlTmp.u.Lo = rgulQuo[0];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
+ rgulQuo[0] = sdlTmp.u.Lo;
+ // 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.u.Hi > 5) || ((sdlTmp.u.Hi == 5) && ( fRemainder || (rgulQuo[0] & 1)))) {
+ Add32To96(rgulQuo, 1u);
+ }
+}
+
+
+
+//**********************************************************************
+//
+// VarDecDiv - Decimal Divide
+//
+//**********************************************************************
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+
+FCIMPL2(void, COMDecimal::DoDivideThrow, DECIMAL * pdecL, DECIMAL * pdecR)
+{
+ FCALL_CONTRACT;
+
+ ULONG rgulQuo[3];
+ ULONG rgulQuoSave[3];
+ ULONG rgulRem[4];
+ ULONG rgulDivisor[3];
+ ULONG ulPwr;
+ ULONG ulTmp;
+ ULONG ulTmp1;
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlDivisor;
+ int iScale;
+ int iCurScale;
+ BOOL fUnscale;
+
+ iScale = DECIMAL_SCALE(*pdecL) - DECIMAL_SCALE(*pdecR);
+ fUnscale = FALSE;
+ rgulDivisor[0] = DECIMAL_LO32(*pdecR);
+ rgulDivisor[1] = DECIMAL_MID32(*pdecR);
+ rgulDivisor[2] = DECIMAL_HI32(*pdecR);
+
+ if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) {
+ // Divisor is only 32 bits. Easy divide.
+ //
+ if (rgulDivisor[0] == 0)
+ FCThrowVoid(kDivideByZeroException);
+
+ rgulQuo[0] = DECIMAL_LO32(*pdecL);
+ rgulQuo[1] = DECIMAL_MID32(*pdecL);
+ rgulQuo[2] = DECIMAL_HI32(*pdecL);
+ rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]);
+
+ for (;;) {
+ if (rgulRem[0] == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale;
+ }
+ break;
+ }
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // We have computed a quotient based on the natural scale
+ // ( <dividend scale> - <divisor scale> ). We have a non-zero
+ // remainder, so now we should increase the scale if possible to
+ // include more quotient bits.
+ //
+ // If it doesn't cause overflow, we'll loop scaling by 10^9 and
+ // computing more quotient bits as long as the remainder stays
+ // non-zero. If scaling by that much would cause overflow, we'll
+ // drop out of the loop and scale by as much as we can.
+ //
+ // Scaling by 10^9 will overflow if rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9
+ // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and
+ // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since
+ // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888
+ // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is
+ // assured not to overflow.
+ //
+ 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 = rgulRem[0] << 1;
+ if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] &&
+ (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) {
+RoundUp:
+ if (!Add32To96(rgulQuo, 1)) {
+ if (iScale == 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, TRUE);
+ break;
+ }
+ }
+ break;
+ }
+
+ if (iCurScale < 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+HaveScale:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+
+ sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]);
+ rgulRem[0] = sdlTmp.u.Hi;
+
+ if (!Add32To96(rgulQuo, sdlTmp.u.Lo)) {
+ if (iScale == 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0));
+ break;
+ }
+ } // for (;;)
+ }
+ else {
+ // Divisor has bits set in the upper 64 bits.
+ //
+ // Divisor must be fully normalized (shifted so bit 31 of the most
+ // significant ULONG is 1). Locate the MSB so we know how much to
+ // normalize by. The dividend will be shifted by the same amount so
+ // the quotient is not changed.
+ //
+ if (rgulDivisor[2] == 0)
+ ulTmp = rgulDivisor[1];
+ else
+ ulTmp = rgulDivisor[2];
+
+ iCurScale = 0;
+ if (!(ulTmp & 0xFFFF0000)) {
+ iCurScale += 16;
+ ulTmp <<= 16;
+ }
+ if (!(ulTmp & 0xFF000000)) {
+ iCurScale += 8;
+ ulTmp <<= 8;
+ }
+ if (!(ulTmp & 0xF0000000)) {
+ iCurScale += 4;
+ ulTmp <<= 4;
+ }
+ if (!(ulTmp & 0xC0000000)) {
+ iCurScale += 2;
+ ulTmp <<= 2;
+ }
+ if (!(ulTmp & 0x80000000)) {
+ iCurScale++;
+ ulTmp <<= 1;
+ }
+
+ // Shift both dividend and divisor left by iCurScale.
+ //
+ sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale;
+ rgulRem[0] = sdlTmp.u.Lo;
+ rgulRem[1] = sdlTmp.u.Hi;
+ sdlTmp.u.Lo = DECIMAL_MID32(*pdecL);
+ sdlTmp.u.Hi = DECIMAL_HI32(*pdecL);
+ sdlTmp.int64 <<= iCurScale;
+ rgulRem[2] = sdlTmp.u.Hi;
+ rgulRem[3] = (DECIMAL_HI32(*pdecL) >> (31 - iCurScale)) >> 1;
+
+ sdlDivisor.u.Lo = rgulDivisor[0];
+ sdlDivisor.u.Hi = 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.u.Lo = rgulRem[2];
+ sdlTmp.u.Hi = rgulRem[3];
+
+ rgulQuo[2] = 0;
+ rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor);
+ rgulQuo[0] = Div96By64(rgulRem, sdlDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale64;
+ }
+ break;
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ 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.
+ //
+ sdlTmp.u.Lo = rgulRem[0];
+ sdlTmp.u.Hi = rgulRem[1];
+ if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
+ (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1)))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale < 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+HaveScale64:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+
+ rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96
+ IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div96By64(rgulRem, sdlDivisor);
+ if (!Add32To96(rgulQuo, ulTmp)) {
+ if (iScale == 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0));
+ break;
+ }
+
+ } // for (;;)
+ }
+ else {
+ // Have a 96-bit divisor in rgulDivisor[].
+ //
+ // Start by finishing the shift left by iCurScale.
+ //
+ sdlTmp.u.Lo = rgulDivisor[1];
+ sdlTmp.u.Hi = rgulDivisor[2];
+ sdlTmp.int64 <<= iCurScale;
+ rgulDivisor[0] = sdlDivisor.u.Lo;
+ rgulDivisor[1] = sdlDivisor.u.Hi;
+ rgulDivisor[2] = sdlTmp.u.Hi;
+
+ // The remainder (currently 96 bits spread over 4 ULONGs)
+ // will be < divisor.
+ //
+ rgulQuo[2] = 0;
+ rgulQuo[1] = 0;
+ rgulQuo[0] = Div128By96(rgulRem, rgulDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale96;
+ }
+ break;
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ 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 (rgulRem[2] >= 0x80000000)
+ goto RoundUp;
+
+ ulTmp = rgulRem[0] > 0x80000000;
+ ulTmp1 = rgulRem[1] > 0x80000000;
+ 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)))))))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale < 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+HaveScale96:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+
+ rgulRem[3] = IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div128By96(rgulRem, rgulDivisor);
+ if (!Add32To96(rgulQuo, ulTmp)) {
+ if (iScale == 0) {
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal"));
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0));
+ break;
+ }
+
+ } // for (;;)
+ }
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ if (fUnscale) {
+ // Try extracting any extra powers of 10 we may have
+ // added. We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1.
+ // If a division by one of these powers returns a zero remainder, then
+ // we keep the quotient. If the remainder is not zero, then we restore
+ // the previous value.
+ //
+ // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10
+ // we can extract. We use this as a quick test on whether to try a
+ // given power.
+ //
+ while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100000000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 8;
+ }
+ else
+ break;
+ }
+
+ if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 4;
+ }
+ }
+
+ if ((rgulQuo[0] & 3) == 0 && iScale >= 2) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 2;
+ }
+ }
+
+ if ((rgulQuo[0] & 1) == 0 && iScale >= 1) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 1;
+ }
+ }
+ }
+
+ DECIMAL_SIGN(*pdecL) = DECIMAL_SIGN(*pdecL) ^ DECIMAL_SIGN(*pdecR);
+ DECIMAL_HI32(*pdecL) = rgulQuo[2];
+ DECIMAL_MID32(*pdecL) = rgulQuo[1];
+ DECIMAL_LO32(*pdecL) = rgulQuo[0];
+ DECIMAL_SCALE(*pdecL) = (BYTE)iScale;
+
+ pdecL->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+
+FCIMPL3(void, COMDecimal::DoDivide, DECIMAL * pdecL, DECIMAL * pdecR, CLR_BOOL * overflowed)
+{
+ FCALL_CONTRACT;
+
+ ULONG rgulQuo[3];
+ ULONG rgulQuoSave[3];
+ ULONG rgulRem[4];
+ ULONG rgulDivisor[3];
+ ULONG ulPwr;
+ ULONG ulTmp;
+ ULONG ulTmp1;
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlDivisor;
+ int iScale;
+ int iCurScale;
+ BOOL fUnscale;
+
+ iScale = DECIMAL_SCALE(*pdecL) - DECIMAL_SCALE(*pdecR);
+ fUnscale = FALSE;
+ rgulDivisor[0] = DECIMAL_LO32(*pdecR);
+ rgulDivisor[1] = DECIMAL_MID32(*pdecR);
+ rgulDivisor[2] = DECIMAL_HI32(*pdecR);
+
+ if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) {
+ // Divisor is only 32 bits. Easy divide.
+ //
+ if (rgulDivisor[0] == 0)
+ FCThrowVoid(kDivideByZeroException);
+
+ rgulQuo[0] = DECIMAL_LO32(*pdecL);
+ rgulQuo[1] = DECIMAL_MID32(*pdecL);
+ rgulQuo[2] = DECIMAL_HI32(*pdecL);
+ rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]);
+
+ for (;;) {
+ if (rgulRem[0] == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale;
+ }
+ break;
+ }
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // We have computed a quotient based on the natural scale
+ // ( <dividend scale> - <divisor scale> ). We have a non-zero
+ // remainder, so now we should increase the scale if possible to
+ // include more quotient bits.
+ //
+ // If it doesn't cause overflow, we'll loop scaling by 10^9 and
+ // computing more quotient bits as long as the remainder stays
+ // non-zero. If scaling by that much would cause overflow, we'll
+ // drop out of the loop and scale by as much as we can.
+ //
+ // Scaling by 10^9 will overflow if rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9
+ // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and
+ // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since
+ // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888
+ // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is
+ // assured not to overflow.
+ //
+ 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 = rgulRem[0] << 1;
+ if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] &&
+ (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) {
+RoundUp:
+ if (!Add32To96(rgulQuo, 1)) {
+ if (iScale == 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, TRUE);
+ break;
+ }
+ }
+ break;
+ }
+
+ if (iCurScale < 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+HaveScale:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+
+ sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]);
+ rgulRem[0] = sdlTmp.u.Hi;
+
+ if (!Add32To96(rgulQuo, sdlTmp.u.Lo)) {
+ if (iScale == 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0));
+ break;
+ }
+ } // for (;;)
+ }
+ else {
+ // Divisor has bits set in the upper 64 bits.
+ //
+ // Divisor must be fully normalized (shifted so bit 31 of the most
+ // significant ULONG is 1). Locate the MSB so we know how much to
+ // normalize by. The dividend will be shifted by the same amount so
+ // the quotient is not changed.
+ //
+ if (rgulDivisor[2] == 0)
+ ulTmp = rgulDivisor[1];
+ else
+ ulTmp = rgulDivisor[2];
+
+ iCurScale = 0;
+ if (!(ulTmp & 0xFFFF0000)) {
+ iCurScale += 16;
+ ulTmp <<= 16;
+ }
+ if (!(ulTmp & 0xFF000000)) {
+ iCurScale += 8;
+ ulTmp <<= 8;
+ }
+ if (!(ulTmp & 0xF0000000)) {
+ iCurScale += 4;
+ ulTmp <<= 4;
+ }
+ if (!(ulTmp & 0xC0000000)) {
+ iCurScale += 2;
+ ulTmp <<= 2;
+ }
+ if (!(ulTmp & 0x80000000)) {
+ iCurScale++;
+ ulTmp <<= 1;
+ }
+
+ // Shift both dividend and divisor left by iCurScale.
+ //
+ sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale;
+ rgulRem[0] = sdlTmp.u.Lo;
+ rgulRem[1] = sdlTmp.u.Hi;
+ sdlTmp.u.Lo = DECIMAL_MID32(*pdecL);
+ sdlTmp.u.Hi = DECIMAL_HI32(*pdecL);
+ sdlTmp.int64 <<= iCurScale;
+ rgulRem[2] = sdlTmp.u.Hi;
+ rgulRem[3] = (DECIMAL_HI32(*pdecL) >> (31 - iCurScale)) >> 1;
+
+ sdlDivisor.u.Lo = rgulDivisor[0];
+ sdlDivisor.u.Hi = 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.u.Lo = rgulRem[2];
+ sdlTmp.u.Hi = rgulRem[3];
+
+ rgulQuo[2] = 0;
+ rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor);
+ rgulQuo[0] = Div96By64(rgulRem, sdlDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale64;
+ }
+ break;
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ 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.
+ //
+ sdlTmp.u.Lo = rgulRem[0];
+ sdlTmp.u.Hi = rgulRem[1];
+ if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
+ (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1)))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale < 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+HaveScale64:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+
+ rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96
+ IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div96By64(rgulRem, sdlDivisor);
+ if (!Add32To96(rgulQuo, ulTmp)) {
+ if (iScale == 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0));
+ break;
+ }
+
+ } // for (;;)
+ }
+ else {
+ // Have a 96-bit divisor in rgulDivisor[].
+ //
+ // Start by finishing the shift left by iCurScale.
+ //
+ sdlTmp.u.Lo = rgulDivisor[1];
+ sdlTmp.u.Hi = rgulDivisor[2];
+ sdlTmp.int64 <<= iCurScale;
+ rgulDivisor[0] = sdlDivisor.u.Lo;
+ rgulDivisor[1] = sdlDivisor.u.Hi;
+ rgulDivisor[2] = sdlTmp.u.Hi;
+
+ // The remainder (currently 96 bits spread over 4 ULONGs)
+ // will be < divisor.
+ //
+ rgulQuo[2] = 0;
+ rgulQuo[1] = 0;
+ rgulQuo[0] = Div128By96(rgulRem, rgulDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale96;
+ }
+ break;
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ fUnscale = TRUE;
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ 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 (rgulRem[2] >= 0x80000000)
+ goto RoundUp;
+
+ ulTmp = rgulRem[0] > 0x80000000;
+ ulTmp1 = rgulRem[1] > 0x80000000;
+ 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)))))))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale < 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+HaveScale96:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+ rgulRem[3] = IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div128By96(rgulRem, rgulDivisor);
+ if (!Add32To96(rgulQuo, ulTmp)) {
+ if (iScale == 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0));
+ break;
+ }
+
+ } // for (;;)
+ }
+ }
+
+ // We need to unscale if and only if we have a non-zero remainder
+ if (fUnscale) {
+ // Try extracting any extra powers of 10 we may have
+ // added. We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1.
+ // If a division by one of these powers returns a zero remainder, then
+ // we keep the quotient. If the remainder is not zero, then we restore
+ // the previous value.
+ //
+ // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10
+ // we can extract. We use this as a quick test on whether to try a
+ // given power.
+ //
+ while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100000000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 8;
+ }
+ else
+ break;
+ }
+
+ if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 4;
+ }
+ }
+
+ if ((rgulQuo[0] & 3) == 0 && iScale >= 2) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 2;
+ }
+ }
+
+ if ((rgulQuo[0] & 1) == 0 && iScale >= 1) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 1;
+ }
+ }
+ }
+
+ DECIMAL_SIGN(*pdecL) = DECIMAL_SIGN(*pdecL) ^ DECIMAL_SIGN(*pdecR);
+ DECIMAL_HI32(*pdecL) = rgulQuo[2];
+ DECIMAL_MID32(*pdecL) = rgulQuo[1];
+ DECIMAL_LO32(*pdecL) = rgulQuo[0];
+ DECIMAL_SCALE(*pdecL) = (BYTE)iScale;
+
+ pdecL->wReserved = 0;
+ *overflowed = false;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
+//**********************************************************************
+//
+// VarDecAdd - Decimal Addition
+// VarDecSub - Decimal Subtraction
+//
+//**********************************************************************
+
+static const ULONG ulTenToNine = 1000000000;
+
+/***
+* ScaleResult
+*
+* Entry:
+* 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:
+* See if we need to scale the result to fit it in 96 bits.
+* Perform needed scaling. Adjust scale factor accordingly.
+*
+* Exit:
+* rgulRes updated in place, always 3 ULONGs.
+* New scale factor returned, -1 if overflow error.
+*
+***********************************************************************/
+
+int ScaleResult(ULONG *rgulRes, int iHiRes, int iScale)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ int iNewScale;
+ int iCur;
+ ULONG ulPwr;
+ ULONG ulTmp;
+ ULONG ulSticky;
+ SPLIT64 sdlTmp;
+
+ // See if we need to scale the result. The combined scale must
+ // be <= DEC_SCALE_MAX and the upper 96 bits must be zero.
+ //
+ // Start by figuring a lower bound on the scaling needed to make
+ // the upper 96 bits zero. iHiRes is the index into rgulRes[]
+ // of the highest non-zero ULONG.
+ //
+ iNewScale = iHiRes * 32 - 64 - 1;
+ if (iNewScale > 0) {
+
+ // Find the MSB.
+ //
+ ulTmp = rgulRes[iHiRes];
+ if (!(ulTmp & 0xFFFF0000)) {
+ iNewScale -= 16;
+ ulTmp <<= 16;
+ }
+ if (!(ulTmp & 0xFF000000)) {
+ iNewScale -= 8;
+ ulTmp <<= 8;
+ }
+ if (!(ulTmp & 0xF0000000)) {
+ iNewScale -= 4;
+ ulTmp <<= 4;
+ }
+ if (!(ulTmp & 0xC0000000)) {
+ iNewScale -= 2;
+ ulTmp <<= 2;
+ }
+ if (!(ulTmp & 0x80000000)) {
+ iNewScale--;
+ ulTmp <<= 1;
+ }
+
+ // 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
+ // with a multiply saves a 96-byte lookup table. The power returned
+ // is <= the power of the number, so we must add one power of 10
+ // to make it's integer part zero after dividing by 256.
+ //
+ // Note: the result of this multiplication by an approximation of
+ // log10(2) have been exhaustively checked to verify it gives the
+ // correct result. (There were only 95 to check...)
+ //
+ iNewScale = ((iNewScale * 77) >> 8) + 1;
+
+ // iNewScale = min scale factor to make high 96 bits zero, 0 - 29.
+ // This reduces the scale factor of the result. If it exceeds the
+ // current scale of the result, we'll overflow.
+ //
+ if (iNewScale > iScale)
+ return -1;
+ }
+ else
+ iNewScale = 0;
+
+ // Make sure we scale by enough to bring the current scale factor
+ // into valid range.
+ //
+ if (iNewScale < iScale - DEC_SCALE_MAX)
+ iNewScale = iScale - DEC_SCALE_MAX;
+
+ if (iNewScale != 0) {
+ // Scale by the power of 10 given by iNewScale. Note that this is
+ // NOT guaranteed to bring the number within 96 bits -- it could
+ // be 1 power of 10 short.
+ //
+ iScale -= iNewScale;
+ ulSticky = 0;
+ sdlTmp.u.Hi = 0; // initialize remainder
+
+ for (;;) {
+
+ ulSticky |= sdlTmp.u.Hi; // record remainder as sticky bit
+
+ if (iNewScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iNewScale];
+
+ // Compute first quotient.
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlTmp.int64 = DivMod64by32(rgulRes[iHiRes], ulPwr);
+ rgulRes[iHiRes] = sdlTmp.u.Lo;
+ iCur = iHiRes - 1;
+
+ if (iCur >= 0) {
+ // If first quotient was 0, update iHiRes.
+ //
+ if (sdlTmp.u.Lo == 0)
+ iHiRes--;
+
+ // Compute subsequent quotients.
+ //
+ do {
+ sdlTmp.u.Lo = rgulRes[iCur];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
+ rgulRes[iCur] = sdlTmp.u.Lo;
+ iCur--;
+ } while (iCur >= 0);
+
+ }
+
+ iNewScale -= POWER10_MAX;
+ if (iNewScale > 0)
+ continue; // scale some more
+
+ // If we scaled enough, iHiRes would be 2 or less. If not,
+ // divide by 10 more.
+ //
+ if (iHiRes > 2) {
+ iNewScale = 1;
+ iScale--;
+ continue; // scale by 10
+ }
+
+ // Round final result. See if remainder >= 1/2 of divisor.
+ // If remainder == 1/2 divisor, round up if odd or sticky bit set.
+ //
+ ulPwr >>= 1; // power of 10 always even
+ if ( ulPwr <= sdlTmp.u.Hi && (ulPwr < sdlTmp.u.Hi ||
+ ((rgulRes[0] & 1) | ulSticky)) ) {
+ iCur = -1;
+ while (++rgulRes[++iCur] == 0);
+
+ if (iCur > 2) {
+ // The rounding caused us to carry beyond 96 bits.
+ // Scale by 10 more.
+ //
+ iHiRes = iCur;
+ ulSticky = 0; // no sticky bit
+ sdlTmp.u.Hi = 0; // or remainder
+ iNewScale = 1;
+ iScale--;
+ continue; // scale by 10
+ }
+ }
+
+ // 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;
+}
+
+FCIMPL3(void, COMDecimal::DoAddSubThrow, DECIMAL * pdecL, DECIMAL * pdecR, UINT8 bSign)
+{
+ FCALL_CONTRACT;
+
+ ULONG rgulNum[6];
+ ULONG ulPwr;
+ int iScale;
+ int iHiProd;
+ int iCur;
+ SPLIT64 sdlTmp;
+ DECIMAL decRes;
+ DECIMAL decTmp;
+ LPDECIMAL pdecTmp;
+ LPDECIMAL pdecLOriginal;
+
+ _ASSERTE(bSign == 0 || bSign == DECIMAL_NEG);
+
+ pdecLOriginal = pdecL;
+
+ bSign ^= (DECIMAL_SIGN(*pdecR) ^ DECIMAL_SIGN(*pdecL)) & DECIMAL_NEG;
+
+ if (DECIMAL_SCALE(*pdecR) == DECIMAL_SCALE(*pdecL)) {
+ // Scale factors are equal, no alignment necessary.
+ //
+ DECIMAL_SIGNSCALE(decRes) = DECIMAL_SIGNSCALE(*pdecL);
+
+AlignedAdd:
+ if (bSign) {
+ // Signs differ - subtract
+ //
+ DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) {
+ DECIMAL_HI32(decRes)--;
+ if (DECIMAL_HI32(decRes) >= DECIMAL_HI32(*pdecL))
+ goto SignFlip;
+ }
+ else if (DECIMAL_HI32(decRes) > DECIMAL_HI32(*pdecL)) {
+ // Got negative result. Flip its sign.
+ //
+SignFlip:
+ DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes));
+ DECIMAL_HI32(decRes) = ~DECIMAL_HI32(decRes);
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ DECIMAL_HI32(decRes)++;
+ DECIMAL_SIGN(decRes) ^= DECIMAL_NEG;
+ }
+
+ }
+ else {
+ // Signs are the same - add
+ //
+ DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) + DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) {
+ DECIMAL_HI32(decRes)++;
+ if (DECIMAL_HI32(decRes) <= DECIMAL_HI32(*pdecL))
+ goto AlignedScale;
+ }
+ else if (DECIMAL_HI32(decRes) < DECIMAL_HI32(*pdecL)) {
+AlignedScale:
+ // The addition carried above 96 bits. Divide the result by 10,
+ // dropping the scale factor.
+ //
+ if (DECIMAL_SCALE(decRes) == 0)
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); // DISP_E_OVERFLOW
+ DECIMAL_SCALE(decRes)--;
+
+ sdlTmp.u.Lo = DECIMAL_HI32(decRes);
+ sdlTmp.u.Hi = 1;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_HI32(decRes) = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = DECIMAL_MID32(decRes);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_MID32(decRes) = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = DECIMAL_LO32(decRes);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_LO32(decRes) = sdlTmp.u.Lo;
+
+ // See if we need to round up.
+ //
+ if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(decRes) & 1))) {
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1);
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ DECIMAL_HI32(decRes)++;
+ }
+ }
+ }
+ }
+ else {
+ // 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. The result will have the larger
+ // scale factor.
+ //
+ DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecR); // scale factor of "smaller"
+ DECIMAL_SIGN(decRes) = DECIMAL_SIGN(*pdecL); // but sign of "larger"
+ iScale = DECIMAL_SCALE(decRes)- DECIMAL_SCALE(*pdecL);
+
+ if (iScale < 0) {
+ // Guessed scale factor wrong. Swap operands.
+ //
+ iScale = -iScale;
+ DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecL);
+ DECIMAL_SIGN(decRes) ^= bSign;
+ pdecTmp = pdecR;
+ pdecR = pdecL;
+ pdecL = pdecTmp;
+ }
+
+ // *pdecL will need to be multiplied by 10^iScale so
+ // it will have the same scale as *pdecR. We could be
+ // extending it to up to 192 bits of precision.
+ //
+ if (iScale <= POWER10_MAX) {
+ // Scaling won't make it larger than 4 ULONGs
+ //
+ ulPwr = rgulPower10[iScale];
+ DECIMAL_LO64_SET(decTmp, UInt32x32To64(DECIMAL_LO32(*pdecL), ulPwr));
+ sdlTmp.int64 = UInt32x32To64(DECIMAL_MID32(*pdecL), ulPwr);
+ sdlTmp.int64 += DECIMAL_MID32(decTmp);
+ DECIMAL_MID32(decTmp) = sdlTmp.u.Lo;
+ DECIMAL_HI32(decTmp) = sdlTmp.u.Hi;
+ sdlTmp.int64 = UInt32x32To64(DECIMAL_HI32(*pdecL), ulPwr);
+ sdlTmp.int64 += DECIMAL_HI32(decTmp);
+ if (sdlTmp.u.Hi == 0) {
+ // Result fits in 96 bits. Use standard aligned add.
+ //
+ DECIMAL_HI32(decTmp) = sdlTmp.u.Lo;
+ pdecL = &decTmp;
+ goto AlignedAdd;
+ }
+ rgulNum[0] = DECIMAL_LO32(decTmp);
+ rgulNum[1] = DECIMAL_MID32(decTmp);
+ rgulNum[2] = sdlTmp.u.Lo;
+ rgulNum[3] = sdlTmp.u.Hi;
+ 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] = DECIMAL_LO32(*pdecL);
+ rgulNum[1] = DECIMAL_MID32(*pdecL);
+ rgulNum[2] = DECIMAL_HI32(*pdecL);
+ iHiProd = 2;
+
+ // Scan for zeros in the upper words.
+ //
+ if (rgulNum[2] == 0) {
+ iHiProd = 1;
+ if (rgulNum[1] == 0) {
+ iHiProd = 0;
+ if (rgulNum[0] == 0) {
+ // Left arg is zero, return right.
+ //
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecR);
+ DECIMAL_SIGN(decRes) ^= bSign;
+ goto RetDec;
+ }
+ }
+ }
+
+ // Scaling loop, up to 10^9 at a time. iHiProd stays updated
+ // with index of highest non-zero ULONG.
+ //
+ for (; iScale > 0; iScale -= POWER10_MAX) {
+ if (iScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iScale];
+
+ sdlTmp.u.Hi = 0;
+ for (iCur = 0; iCur <= iHiProd; iCur++) {
+ sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi;
+ rgulNum[iCur] = sdlTmp.u.Lo;
+ }
+
+ if (sdlTmp.u.Hi != 0)
+ // We're extending the result by another ULONG.
+ rgulNum[++iHiProd] = sdlTmp.u.Hi;
+ }
+ }
+
+ // Scaling complete, do the add. Could be subtract if signs differ.
+ //
+ sdlTmp.u.Lo = rgulNum[0];
+ sdlTmp.u.Hi = rgulNum[1];
+
+ if (bSign) {
+ // Signs differ, subtract.
+ //
+ DECIMAL_LO64_SET(decRes, (sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = rgulNum[2] - DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) {
+ DECIMAL_HI32(decRes)--;
+ if (DECIMAL_HI32(decRes) >= rgulNum[2])
+ goto LongSub;
+ }
+ else if (DECIMAL_HI32(decRes) > rgulNum[2]) {
+LongSub:
+ // 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)
+ goto SignFlip;
+
+ iCur = 3;
+ while(rgulNum[iCur++]-- == 0);
+ if (rgulNum[iHiProd] == 0)
+ iHiProd--;
+ }
+ }
+ else {
+ // Signs the same, add.
+ //
+ DECIMAL_LO64_SET(decRes, (sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = rgulNum[2] + DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) {
+ DECIMAL_HI32(decRes)++;
+ if (DECIMAL_HI32(decRes) <= rgulNum[2])
+ goto LongAdd;
+ }
+ else if (DECIMAL_HI32(decRes) < rgulNum[2]) {
+LongAdd:
+ // Had a carry above 96 bits.
+ //
+ iCur = 3;
+ do {
+ if (iHiProd < iCur) {
+ rgulNum[iCur] = 1;
+ iHiProd = iCur;
+ break;
+ }
+ }while (++rgulNum[iCur++] == 0);
+ }
+ }
+
+ if (iHiProd > 2) {
+ rgulNum[0] = DECIMAL_LO32(decRes);
+ rgulNum[1] = DECIMAL_MID32(decRes);
+ rgulNum[2] = DECIMAL_HI32(decRes);
+ DECIMAL_SCALE(decRes) = (BYTE)ScaleResult(rgulNum, iHiProd, DECIMAL_SCALE(decRes));
+ if (DECIMAL_SCALE(decRes) == (BYTE)-1)
+ FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); // DISP_E_OVERFLOW
+
+ DECIMAL_LO32(decRes) = rgulNum[0];
+ DECIMAL_MID32(decRes) = rgulNum[1];
+ DECIMAL_HI32(decRes) = rgulNum[2];
+ }
+ }
+
+RetDec:
+ pdecL = pdecLOriginal;
+ COPYDEC(*pdecL, decRes)
+ pdecL->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
+FCIMPL4(void, COMDecimal::DoAddSub, DECIMAL * pdecL, DECIMAL * pdecR, UINT8 bSign, CLR_BOOL * overflowed)
+{
+ FCALL_CONTRACT;
+
+ ULONG rgulNum[6];
+ ULONG ulPwr;
+ int iScale;
+ int iHiProd;
+ int iCur;
+ SPLIT64 sdlTmp;
+ DECIMAL decRes;
+ DECIMAL decTmp;
+ LPDECIMAL pdecTmp;
+ LPDECIMAL pdecLOriginal;
+
+ _ASSERTE(bSign == 0 || bSign == DECIMAL_NEG);
+
+ pdecLOriginal = pdecL;
+
+ bSign ^= (DECIMAL_SIGN(*pdecR) ^ DECIMAL_SIGN(*pdecL)) & DECIMAL_NEG;
+
+ if (DECIMAL_SCALE(*pdecR) == DECIMAL_SCALE(*pdecL)) {
+ // Scale factors are equal, no alignment necessary.
+ //
+ DECIMAL_SIGNSCALE(decRes) = DECIMAL_SIGNSCALE(*pdecL);
+
+AlignedAdd:
+ if (bSign) {
+ // Signs differ - subtract
+ //
+ DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) {
+ DECIMAL_HI32(decRes)--;
+ if (DECIMAL_HI32(decRes) >= DECIMAL_HI32(*pdecL))
+ goto SignFlip;
+ }
+ else if (DECIMAL_HI32(decRes) > DECIMAL_HI32(*pdecL)) {
+ // Got negative result. Flip its sign.
+ //
+SignFlip:
+ DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes));
+ DECIMAL_HI32(decRes) = ~DECIMAL_HI32(decRes);
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ DECIMAL_HI32(decRes)++;
+ DECIMAL_SIGN(decRes) ^= DECIMAL_NEG;
+ }
+
+ }
+ else {
+ // Signs are the same - add
+ //
+ DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) + DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) {
+ DECIMAL_HI32(decRes)++;
+ if (DECIMAL_HI32(decRes) <= DECIMAL_HI32(*pdecL))
+ goto AlignedScale;
+ }
+ else if (DECIMAL_HI32(decRes) < DECIMAL_HI32(*pdecL)) {
+AlignedScale:
+ // The addition carried above 96 bits. Divide the result by 10,
+ // dropping the scale factor.
+ //
+ if (DECIMAL_SCALE(decRes) == 0) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+ DECIMAL_SCALE(decRes)--;
+
+ sdlTmp.u.Lo = DECIMAL_HI32(decRes);
+ sdlTmp.u.Hi = 1;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_HI32(decRes) = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = DECIMAL_MID32(decRes);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_MID32(decRes) = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = DECIMAL_LO32(decRes);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ DECIMAL_LO32(decRes) = sdlTmp.u.Lo;
+
+ // See if we need to round up.
+ //
+ if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(decRes) & 1))) {
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1);
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ DECIMAL_HI32(decRes)++;
+ }
+ }
+ }
+ }
+ else {
+ // 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. The result will have the larger
+ // scale factor.
+ //
+ DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecR); // scale factor of "smaller"
+ DECIMAL_SIGN(decRes) = DECIMAL_SIGN(*pdecL); // but sign of "larger"
+ iScale = DECIMAL_SCALE(decRes)- DECIMAL_SCALE(*pdecL);
+
+ if (iScale < 0) {
+ // Guessed scale factor wrong. Swap operands.
+ //
+ iScale = -iScale;
+ DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecL);
+ DECIMAL_SIGN(decRes) ^= bSign;
+ pdecTmp = pdecR;
+ pdecR = pdecL;
+ pdecL = pdecTmp;
+ }
+
+ // *pdecL will need to be multiplied by 10^iScale so
+ // it will have the same scale as *pdecR. We could be
+ // extending it to up to 192 bits of precision.
+ //
+ if (iScale <= POWER10_MAX) {
+ // Scaling won't make it larger than 4 ULONGs
+ //
+ ulPwr = rgulPower10[iScale];
+ DECIMAL_LO64_SET(decTmp, UInt32x32To64(DECIMAL_LO32(*pdecL), ulPwr));
+ sdlTmp.int64 = UInt32x32To64(DECIMAL_MID32(*pdecL), ulPwr);
+ sdlTmp.int64 += DECIMAL_MID32(decTmp);
+ DECIMAL_MID32(decTmp) = sdlTmp.u.Lo;
+ DECIMAL_HI32(decTmp) = sdlTmp.u.Hi;
+ sdlTmp.int64 = UInt32x32To64(DECIMAL_HI32(*pdecL), ulPwr);
+ sdlTmp.int64 += DECIMAL_HI32(decTmp);
+ if (sdlTmp.u.Hi == 0) {
+ // Result fits in 96 bits. Use standard aligned add.
+ //
+ DECIMAL_HI32(decTmp) = sdlTmp.u.Lo;
+ pdecL = &decTmp;
+ goto AlignedAdd;
+ }
+ rgulNum[0] = DECIMAL_LO32(decTmp);
+ rgulNum[1] = DECIMAL_MID32(decTmp);
+ rgulNum[2] = sdlTmp.u.Lo;
+ rgulNum[3] = sdlTmp.u.Hi;
+ 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] = DECIMAL_LO32(*pdecL);
+ rgulNum[1] = DECIMAL_MID32(*pdecL);
+ rgulNum[2] = DECIMAL_HI32(*pdecL);
+ iHiProd = 2;
+
+ // Scan for zeros in the upper words.
+ //
+ if (rgulNum[2] == 0) {
+ iHiProd = 1;
+ if (rgulNum[1] == 0) {
+ iHiProd = 0;
+ if (rgulNum[0] == 0) {
+ // Left arg is zero, return right.
+ //
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecR);
+ DECIMAL_SIGN(decRes) ^= bSign;
+ goto RetDec;
+ }
+ }
+ }
+
+ // Scaling loop, up to 10^9 at a time. iHiProd stays updated
+ // with index of highest non-zero ULONG.
+ //
+ for (; iScale > 0; iScale -= POWER10_MAX) {
+ if (iScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iScale];
+
+ sdlTmp.u.Hi = 0;
+ for (iCur = 0; iCur <= iHiProd; iCur++) {
+ sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi;
+ rgulNum[iCur] = sdlTmp.u.Lo;
+ }
+
+ if (sdlTmp.u.Hi != 0)
+ // We're extending the result by another ULONG.
+ rgulNum[++iHiProd] = sdlTmp.u.Hi;
+ }
+ }
+
+ // Scaling complete, do the add. Could be subtract if signs differ.
+ //
+ sdlTmp.u.Lo = rgulNum[0];
+ sdlTmp.u.Hi = rgulNum[1];
+
+ if (bSign) {
+ // Signs differ, subtract.
+ //
+ DECIMAL_LO64_SET(decRes, (sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = rgulNum[2] - DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) {
+ DECIMAL_HI32(decRes)--;
+ if (DECIMAL_HI32(decRes) >= rgulNum[2])
+ goto LongSub;
+ }
+ else if (DECIMAL_HI32(decRes) > rgulNum[2]) {
+LongSub:
+ // 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)
+ goto SignFlip;
+
+ iCur = 3;
+ while(rgulNum[iCur++]-- == 0);
+ if (rgulNum[iHiProd] == 0)
+ iHiProd--;
+ }
+ }
+ else {
+ // Signs the same, add.
+ //
+ DECIMAL_LO64_SET(decRes, (sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR)));
+ DECIMAL_HI32(decRes) = rgulNum[2] + DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) {
+ DECIMAL_HI32(decRes)++;
+ if (DECIMAL_HI32(decRes) <= rgulNum[2])
+ goto LongAdd;
+ }
+ else if (DECIMAL_HI32(decRes) < rgulNum[2]) {
+LongAdd:
+ // Had a carry above 96 bits.
+ //
+ iCur = 3;
+ do {
+ if (iHiProd < iCur) {
+ rgulNum[iCur] = 1;
+ iHiProd = iCur;
+ break;
+ }
+ }while (++rgulNum[iCur++] == 0);
+ }
+ }
+
+ if (iHiProd > 2) {
+ rgulNum[0] = DECIMAL_LO32(decRes);
+ rgulNum[1] = DECIMAL_MID32(decRes);
+ rgulNum[2] = DECIMAL_HI32(decRes);
+ DECIMAL_SCALE(decRes) = (BYTE)ScaleResult(rgulNum, iHiProd, DECIMAL_SCALE(decRes));
+ if (DECIMAL_SCALE(decRes) == (BYTE)-1) {
+ *overflowed = true;
+ FC_GC_POLL();
+ return;
+ }
+
+ DECIMAL_LO32(decRes) = rgulNum[0];
+ DECIMAL_MID32(decRes) = rgulNum[1];
+ DECIMAL_HI32(decRes) = rgulNum[2];
+ }
+ }
+
+RetDec:
+ pdecL = pdecLOriginal;
+ COPYDEC(*pdecL, decRes)
+ pdecL->wReserved = 0;
+ FC_GC_POLL();
+}
+FCIMPLEND
+
diff --git a/src/classlibnative/bcltype/decimal.h b/src/classlibnative/bcltype/decimal.h
new file mode 100644
index 0000000000..6ce1fbe677
--- /dev/null
+++ b/src/classlibnative/bcltype/decimal.h
@@ -0,0 +1,54 @@
+// 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.
+//
+// File: Decimal.h
+//
+
+//
+
+#ifndef _DECIMAL_H_
+#define _DECIMAL_H_
+
+#include <oleauto.h>
+
+#include <pshpack1.h>
+
+#include "number.h"
+
+#define DECIMAL_PRECISION 29
+
+class COMDecimal {
+public:
+ static FCDECL2_IV(void, InitSingle, DECIMAL *_this, float value);
+ static FCDECL2_IV(void, InitDouble, DECIMAL *_this, double value);
+ static FCDECL2(INT32, DoCompare, DECIMAL * d1, DECIMAL * d2);
+ static FCDECL1(INT32, GetHashCode, DECIMAL *d);
+
+ static FCDECL3(void, DoAddSubThrow, DECIMAL * d1, DECIMAL * d2, UINT8 bSign);
+ static FCDECL2(void, DoDivideThrow, DECIMAL * d1, DECIMAL * d2);
+ static FCDECL2(void, DoMultiplyThrow, DECIMAL * d1, DECIMAL * d2);
+
+ static FCDECL4(void, DoAddSub, DECIMAL * d1, DECIMAL * d2, UINT8 bSign, CLR_BOOL * overflowed);
+ static FCDECL3(void, DoDivide, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed);
+ static FCDECL3(void, DoMultiply, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed);
+
+ static FCDECL2(void, DoRound, DECIMAL * d1, INT32 decimals);
+ static FCDECL2_IV(void, DoToCurrency, CY * result, DECIMAL d);
+ static FCDECL1(void, DoTruncate, DECIMAL * d);
+ static FCDECL1(void, DoFloor, DECIMAL * d);
+
+ static FCDECL1(double, ToDouble, FC_DECIMAL d);
+ static FCDECL1(float, ToSingle, FC_DECIMAL d);
+ static FCDECL1(INT32, ToInt32, FC_DECIMAL d);
+ static FCDECL1(Object*, ToString, FC_DECIMAL d);
+
+ static void DecimalToNumber(DECIMAL* value, NUMBER* number);
+ static int NumberToDecimal(NUMBER* number, DECIMAL* value);
+
+
+};
+
+#include <poppack.h>
+
+#endif // _DECIMAL_H_
diff --git a/src/classlibnative/bcltype/number.cpp b/src/classlibnative/bcltype/number.cpp
new file mode 100644
index 0000000000..349bffb819
--- /dev/null
+++ b/src/classlibnative/bcltype/number.cpp
@@ -0,0 +1,3005 @@
+// 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.
+//
+// File: Number.cpp
+//
+
+//
+
+#include "common.h"
+#include "excep.h"
+#include "number.h"
+#include "string.h"
+#include "decimal.h"
+#include <stdlib.h>
+
+typedef wchar_t wchar;
+
+#define INT32_PRECISION 10
+#define UINT32_PRECISION INT32_PRECISION
+#define INT64_PRECISION 19
+#define UINT64_PRECISION 20
+#define FLOAT_PRECISION 7
+#define DOUBLE_PRECISION 15
+#define LARGE_BUFFER_SIZE 600
+#define MIN_BUFFER_SIZE 105
+
+#define SCALE_NAN 0x80000000
+#define SCALE_INF 0x7FFFFFFF
+
+struct FPSINGLE {
+#if BIGENDIAN
+ unsigned int sign: 1;
+ unsigned int exp: 8;
+ unsigned int mant: 23;
+#else
+ unsigned int mant: 23;
+ unsigned int exp: 8;
+ unsigned int sign: 1;
+#endif
+};
+
+struct FPDOUBLE {
+#if BIGENDIAN
+ unsigned int sign: 1;
+ unsigned int exp: 11;
+ unsigned int mantHi: 20;
+ unsigned int mantLo;
+#else
+ unsigned int mantLo;
+ unsigned int mantHi: 20;
+ unsigned int exp: 11;
+ unsigned int sign: 1;
+#endif
+};
+
+
+static const char* const posCurrencyFormats[] = {
+ "$#", "#$", "$ #", "# $"};
+
+static const char* const negCurrencyFormats[] = {
+ "($#)", "-$#", "$-#", "$#-",
+ "(#$)", "-#$", "#-$", "#$-",
+ "-# $", "-$ #", "# $-", "$ #-",
+ "$ -#", "#- $", "($ #)", "(# $)"};
+
+static const char* const posPercentFormats[] = {
+ "# %", "#%", "%#", "% #" // Last one is new in Whidbey
+};
+
+static const char* const negPercentFormats[] = {
+ "-# %", "-#%", "-%#",
+ "%-#", "%#-", // Last 9 are new in WHidbey
+ "#-%", "#%-",
+ "-% #", "# %-", "% #-",
+ "% -#", "#- %"
+};
+
+static const char* const negNumberFormats[] = {
+ "(#)", "-#", "- #", "#-", "# -",
+};
+
+static const char posNumberFormat[] = "#";
+
+#if defined(_TARGET_X86_)
+
+extern "C" void _cdecl /*__stdcall*/ DoubleToNumber(double value, int precision, NUMBER* number);
+extern "C" void _cdecl /*__stdcall*/ NumberToDouble(NUMBER* number, double* value);
+
+#pragma warning(disable:4035)
+
+wchar_t* COMNumber::Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov eax,value
+ mov ebx,10
+ mov ecx,digits
+ mov edi,p
+ jmp L2
+L1: xor edx,edx
+ div ebx
+ add edx,'0' //promote dl to edx to avoid partial register stall and LCP stall
+ sub edi,2
+ mov [edi],dx
+L2: dec ecx
+ jge L1
+ or eax,eax
+ jne L1
+ mov eax,edi
+ }
+}
+
+unsigned int Int64DivMod1E9(unsigned __int64* value)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _asm {
+ mov ebx,value
+ mov ecx,1000000000
+ xor edx,edx
+ mov eax,[ebx+4]
+ div ecx
+ mov [ebx+4],eax
+ mov eax,[ebx]
+ div ecx
+ mov [ebx],eax
+ mov eax,edx
+ }
+}
+
+#pragma warning(default:4035)
+
+#else // !(defined(_TARGET_X86_)
+
+#pragma warning(disable:4273)
+extern "C" char* __cdecl _ecvt(double, int, int*, int*);
+
+void DoubleToNumber(double value, int precision, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_DoubleToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_DOUBLE;
+#endif
+
+ number->precision = precision;
+ if (((FPDOUBLE*)&value)->exp == 0x7FF) {
+ number->scale = (((FPDOUBLE*)&value)->mantLo || ((FPDOUBLE*)&value)->mantHi) ? SCALE_NAN: SCALE_INF;
+ number->sign = ((FPDOUBLE*)&value)->sign;
+ number->digits[0] = 0;
+ }
+ else {
+ char* src = _ecvt(value, precision, &number->scale, &number->sign);
+ wchar* dst = number->digits;
+ if (*src != '0') {
+ while (*src) *dst++ = *src++;
+ }
+ *dst = 0;
+ }
+}
+
+/*===========================================================
+ Portable NumberToDouble implementation
+ --------------------------------------
+
+ - does the conversion with the best possible precision.
+ - does not use any float arithmetic so it is not sensitive
+ to differences in precision of floating point calculations
+ across platforms.
+
+ The internal integer representation of the float number is
+ UINT64 mantissa + INT exponent. The mantissa is kept normalized
+ ie with the most significant one being 63-th bit of UINT64.
+===========================================================*/
+
+//
+// get 32-bit integer from at most 9 digits
+//
+static unsigned DigitsToInt(__in_ecount(count) wchar* p, int count)
+{
+ LIMITED_METHOD_CONTRACT
+
+ _ASSERTE(1 <= count && count <= 9);
+ wchar* end = p + count;
+ unsigned res = *p - '0';
+ for ( p = p + 1; p < end; p++) {
+ res = 10 * res + *p - '0';
+ }
+ return res;
+}
+
+//
+// helper macro to multiply two 32-bit uints
+//
+#define Mul32x32To64(a, b) ((UINT64)((UINT32)(a)) * (UINT64)((UINT32)(b)))
+
+
+//
+// multiply two numbers in the internal integer representation
+//
+static UINT64 Mul64Lossy(UINT64 a, UINT64 b, INT* pexp)
+{
+ LIMITED_METHOD_CONTRACT
+
+ // it's ok to losse some precision here - Mul64 will be called
+ // at most twice during the conversion, so the error won't propagate
+ // to any of the 53 significant bits of the result
+ UINT64 val = Mul32x32To64(a >> 32, b >> 32) +
+ (Mul32x32To64(a >> 32, b) >> 32) +
+ (Mul32x32To64(a, b >> 32) >> 32);
+
+ // normalize
+ if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; *pexp -= 1; }
+
+ return val;
+}
+
+//
+// precomputed tables with powers of 10. These allows us to do at most
+// two Mul64 during the conversion. This is important not only
+// for speed, but also for precision because of Mul64 computes with 1 bit error.
+//
+
+static const UINT64 rgval64Power10[] = {
+// powers of 10
+/*1*/ I64(0xa000000000000000),
+/*2*/ I64(0xc800000000000000),
+/*3*/ I64(0xfa00000000000000),
+/*4*/ I64(0x9c40000000000000),
+/*5*/ I64(0xc350000000000000),
+/*6*/ I64(0xf424000000000000),
+/*7*/ I64(0x9896800000000000),
+/*8*/ I64(0xbebc200000000000),
+/*9*/ I64(0xee6b280000000000),
+/*10*/ I64(0x9502f90000000000),
+/*11*/ I64(0xba43b74000000000),
+/*12*/ I64(0xe8d4a51000000000),
+/*13*/ I64(0x9184e72a00000000),
+/*14*/ I64(0xb5e620f480000000),
+/*15*/ I64(0xe35fa931a0000000),
+
+// powers of 0.1
+/*1*/ I64(0xcccccccccccccccd),
+/*2*/ I64(0xa3d70a3d70a3d70b),
+/*3*/ I64(0x83126e978d4fdf3c),
+/*4*/ I64(0xd1b71758e219652e),
+/*5*/ I64(0xa7c5ac471b478425),
+/*6*/ I64(0x8637bd05af6c69b7),
+/*7*/ I64(0xd6bf94d5e57a42be),
+/*8*/ I64(0xabcc77118461ceff),
+/*9*/ I64(0x89705f4136b4a599),
+/*10*/ I64(0xdbe6fecebdedd5c2),
+/*11*/ I64(0xafebff0bcb24ab02),
+/*12*/ I64(0x8cbccc096f5088cf),
+/*13*/ I64(0xe12e13424bb40e18),
+/*14*/ I64(0xb424dc35095cd813),
+/*15*/ I64(0x901d7cf73ab0acdc),
+};
+
+static const INT8 rgexp64Power10[] = {
+// exponents for both powers of 10 and 0.1
+/*1*/ 4,
+/*2*/ 7,
+/*3*/ 10,
+/*4*/ 14,
+/*5*/ 17,
+/*6*/ 20,
+/*7*/ 24,
+/*8*/ 27,
+/*9*/ 30,
+/*10*/ 34,
+/*11*/ 37,
+/*12*/ 40,
+/*13*/ 44,
+/*14*/ 47,
+/*15*/ 50,
+};
+
+static const UINT64 rgval64Power10By16[] = {
+// powers of 10^16
+/*1*/ I64(0x8e1bc9bf04000000),
+/*2*/ I64(0x9dc5ada82b70b59e),
+/*3*/ I64(0xaf298d050e4395d6),
+/*4*/ I64(0xc2781f49ffcfa6d4),
+/*5*/ I64(0xd7e77a8f87daf7fa),
+/*6*/ I64(0xefb3ab16c59b14a0),
+/*7*/ I64(0x850fadc09923329c),
+/*8*/ I64(0x93ba47c980e98cde),
+/*9*/ I64(0xa402b9c5a8d3a6e6),
+/*10*/ I64(0xb616a12b7fe617a8),
+/*11*/ I64(0xca28a291859bbf90),
+/*12*/ I64(0xe070f78d39275566),
+/*13*/ I64(0xf92e0c3537826140),
+/*14*/ I64(0x8a5296ffe33cc92c),
+/*15*/ I64(0x9991a6f3d6bf1762),
+/*16*/ I64(0xaa7eebfb9df9de8a),
+/*17*/ I64(0xbd49d14aa79dbc7e),
+/*18*/ I64(0xd226fc195c6a2f88),
+/*19*/ I64(0xe950df20247c83f8),
+/*20*/ I64(0x81842f29f2cce373),
+/*21*/ I64(0x8fcac257558ee4e2),
+
+// powers of 0.1^16
+/*1*/ I64(0xe69594bec44de160),
+/*2*/ I64(0xcfb11ead453994c3),
+/*3*/ I64(0xbb127c53b17ec165),
+/*4*/ I64(0xa87fea27a539e9b3),
+/*5*/ I64(0x97c560ba6b0919b5),
+/*6*/ I64(0x88b402f7fd7553ab),
+/*7*/ I64(0xf64335bcf065d3a0),
+/*8*/ I64(0xddd0467c64bce4c4),
+/*9*/ I64(0xc7caba6e7c5382ed),
+/*10*/ I64(0xb3f4e093db73a0b7),
+/*11*/ I64(0xa21727db38cb0053),
+/*12*/ I64(0x91ff83775423cc29),
+/*13*/ I64(0x8380dea93da4bc82),
+/*14*/ I64(0xece53cec4a314f00),
+/*15*/ I64(0xd5605fcdcf32e217),
+/*16*/ I64(0xc0314325637a1978),
+/*17*/ I64(0xad1c8eab5ee43ba2),
+/*18*/ I64(0x9becce62836ac5b0),
+/*19*/ I64(0x8c71dcd9ba0b495c),
+/*20*/ I64(0xfd00b89747823938),
+/*21*/ I64(0xe3e27a444d8d991a),
+};
+
+static const INT16 rgexp64Power10By16[] = {
+// exponents for both powers of 10^16 and 0.1^16
+/*1*/ 54,
+/*2*/ 107,
+/*3*/ 160,
+/*4*/ 213,
+/*5*/ 266,
+/*6*/ 319,
+/*7*/ 373,
+/*8*/ 426,
+/*9*/ 479,
+/*10*/ 532,
+/*11*/ 585,
+/*12*/ 638,
+/*13*/ 691,
+/*14*/ 745,
+/*15*/ 798,
+/*16*/ 851,
+/*17*/ 904,
+/*18*/ 957,
+/*19*/ 1010,
+/*20*/ 1064,
+/*21*/ 1117,
+};
+
+#ifdef _DEBUG
+//
+// slower high precision version of Mul64 for computation of the tables
+//
+static UINT64 Mul64Precise(UINT64 a, UINT64 b, INT* pexp)
+{
+ LIMITED_METHOD_CONTRACT
+
+ UINT64 hilo =
+ ((Mul32x32To64(a >> 32, b) >> 1) +
+ (Mul32x32To64(a, b >> 32) >> 1) +
+ (Mul32x32To64(a, b) >> 33)) >> 30;
+
+ UINT64 val = Mul32x32To64(a >> 32, b >> 32) + (hilo >> 1) + (hilo & 1);
+
+ // normalize
+ if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; *pexp -= 1; }
+
+ return val;
+}
+
+
+//
+// debug-only verification of the precomputed tables
+//
+static void CheckTable(UINT64 val, INT exp, LPCVOID table, int size, LPCSTR name, int tabletype)
+{
+ WRAPPER_NO_CONTRACT
+
+ UINT64 multval = val;
+ INT mulexp = exp;
+ bool fBad = false;
+ for (int i = 0; i < size; i++) {
+ switch (tabletype) {
+ case 1:
+ if (((UINT64*)table)[i] != val) {
+ if (!fBad) {
+ fprintf(stderr, "%s:\n", name);
+ fBad = true;
+ }
+ fprintf(stderr, "/*%d*/ I64(0x%I64x),\n", i+1, val);
+ }
+ break;
+ case 2:
+ if (((INT8*)table)[i] != exp) {
+ if (!fBad) {
+ fprintf(stderr, "%s:\n", name);
+ fBad = true;
+ }
+ fprintf(stderr, "/*%d*/ %d,\n", i+1, exp);
+ }
+ break;
+ case 3:
+ if (((INT16*)table)[i] != exp) {
+ if (!fBad) {
+ fprintf(stderr, "%s:\n", name);
+ fBad = true;
+ }
+ fprintf(stderr, "/*%d*/ %d,\n", i+1, exp);
+ }
+ break;
+ default:
+ _ASSERTE(false);
+ break;
+ }
+
+ exp += mulexp;
+ val = Mul64Precise(val, multval, &exp);
+ }
+ _ASSERTE(!fBad || !"NumberToDouble table not correct. Correct version dumped to stderr.");
+}
+
+void CheckTables()
+{
+ WRAPPER_NO_CONTRACT
+
+ UINT64 val; INT exp;
+
+ val = I64(0xa000000000000000); exp = 4; // 10
+ CheckTable(val, exp, rgval64Power10, 15, "rgval64Power10", 1);
+ CheckTable(val, exp, rgexp64Power10, 15, "rgexp64Power10", 2);
+
+ val = I64(0x8e1bc9bf04000000); exp = 54; //10^16
+ CheckTable(val, exp, rgval64Power10By16, 21, "rgval64Power10By16", 1);
+ CheckTable(val, exp, rgexp64Power10By16, 21, "rgexp64Power10By16", 3);
+
+ val = I64(0xCCCCCCCCCCCCCCCD); exp = -3; // 0.1
+ CheckTable(val, exp, rgval64Power10+15, 15, "rgval64Power10 - inv", 1);
+
+ val = I64(0xe69594bec44de160); exp = -53; // 0.1^16
+ CheckTable(val, exp, rgval64Power10By16+21, 21, "rgval64Power10By16 - inv", 1);
+}
+#endif // _DEBUG
+
+void NumberToDouble(NUMBER* number, double* value)
+{
+ WRAPPER_NO_CONTRACT
+
+ UINT64 val;
+ INT exp;
+ wchar* src = number->digits;
+ int remaining;
+ int total;
+ int count;
+ int scale;
+ int absscale;
+ int index;
+
+#ifdef _DEBUG
+ static bool fCheckedTables = false;
+ if (!fCheckedTables) {
+ CheckTables();
+ fCheckedTables = true;
+ }
+#endif // _DEBUG
+
+ total = (int)wcslen(src);
+ remaining = total;
+
+ // skip the leading zeros
+ while (*src == '0') {
+ remaining--;
+ src++;
+ }
+
+ if (remaining == 0) {
+ *value = 0;
+ goto done;
+ }
+
+ count = min(remaining, 9);
+ remaining -= count;
+ val = DigitsToInt(src, count);
+
+ if (remaining > 0) {
+ count = min(remaining, 9);
+ remaining -= count;
+
+ // get the denormalized power of 10
+ UINT32 mult = (UINT32)(rgval64Power10[count-1] >> (64 - rgexp64Power10[count-1]));
+ val = Mul32x32To64(val, mult) + DigitsToInt(src+9, count);
+ }
+
+ scale = number->scale - (total - remaining);
+ absscale = abs(scale);
+ if (absscale >= 22 * 16) {
+ // overflow / underflow
+ *(UINT64*)value = (scale > 0) ? I64(0x7FF0000000000000) : 0;
+ goto done;
+ }
+
+ exp = 64;
+
+ // normalize the mantissa
+ if ((val & I64(0xFFFFFFFF00000000)) == 0) { val <<= 32; exp -= 32; }
+ if ((val & I64(0xFFFF000000000000)) == 0) { val <<= 16; exp -= 16; }
+ if ((val & I64(0xFF00000000000000)) == 0) { val <<= 8; exp -= 8; }
+ if ((val & I64(0xF000000000000000)) == 0) { val <<= 4; exp -= 4; }
+ if ((val & I64(0xC000000000000000)) == 0) { val <<= 2; exp -= 2; }
+ if ((val & I64(0x8000000000000000)) == 0) { val <<= 1; exp -= 1; }
+
+ index = absscale & 15;
+ if (index) {
+ INT multexp = rgexp64Power10[index-1];
+ // the exponents are shared between the inverted and regular table
+ exp += (scale < 0) ? (-multexp + 1) : multexp;
+
+ UINT64 multval = rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1];
+ val = Mul64Lossy(val, multval, &exp);
+ }
+
+ index = absscale >> 4;
+ if (index) {
+ INT multexp = rgexp64Power10By16[index-1];
+ // the exponents are shared between the inverted and regular table
+ exp += (scale < 0) ? (-multexp + 1) : multexp;
+
+ UINT64 multval = rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1];
+ val = Mul64Lossy(val, multval, &exp);
+ }
+
+
+ // round & scale down
+ if ((UINT32)val & (1 << 10))
+ {
+ // IEEE round to even
+ UINT64 tmp = val + ((1 << 10) - 1) + (((UINT32)val >> 11) & 1);
+ if (tmp < val) {
+ // overflow
+ tmp = (tmp >> 1) | I64(0x8000000000000000);
+ exp += 1;
+ }
+ val = tmp;
+ }
+
+ // return the exponent to a biased state
+ exp += 0x3FE;
+
+ // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case
+ if (exp <= 0) {
+ if (exp == -52 && (val >= I64(0x8000000000000058))) {
+ // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero)
+ val = I64(0x0000000000000001);
+ }
+ else if (exp <= -52) {
+ // underflow
+ val = 0;
+ }
+ else {
+ // denormalized
+ val >>= (-exp + 11 + 1);
+ }
+ }
+ else if (exp >= 0x7FF) {
+ // overflow
+ val = I64(0x7FF0000000000000);
+ }
+ else {
+ // normal postive exponent case
+ val = ((UINT64)exp << 52) + ((val >> 11) & I64(0x000FFFFFFFFFFFFF));
+ }
+
+ *(UINT64*)value = val;
+
+done:
+ if (number->sign) *(UINT64*)value |= I64(0x8000000000000000);
+}
+
+wchar_t* COMNumber::Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL);
+
+ while (--digits >= 0 || value != 0) {
+ *--p = value % 10 + '0';
+ value /= 10;
+ }
+ return p;
+}
+
+unsigned int Int64DivMod1E9(unsigned __int64* value)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(value != NULL);
+
+ unsigned int rem = (unsigned int)(*value % 1000000000);
+ *value /= 1000000000;
+ return rem;
+}
+
+
+
+
+#endif // !(defined(_TARGET_X86_)
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#endif
+
+inline void AddStringRef(__in wchar** ppBuffer, STRINGREF strRef)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(ppBuffer != NULL && strRef != NULL);
+
+ wchar* buffer = strRef->GetBuffer();
+ _ASSERTE(buffer != NULL);
+ DWORD length = strRef->GetStringLength();
+ for (wchar* str = buffer; str < buffer + length; (*ppBuffer)++, str++)
+ {
+ **ppBuffer = *str;
+ }
+}
+
+inline wchar* GetDigitsBuffer(NUMBER* number)
+{
+ return (number->allDigits != NULL) ? number->allDigits : number->digits;
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // Go back to command line default optimizations
+#endif
+
+LPCWSTR MatchChars(LPCWSTR p, LPCWSTR str)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL && str != NULL);
+
+ if (!*str) return 0;
+ for (; *str; p++, str++)
+ {
+ if (*p != *str) //We only hurt the failure case
+ {
+ if ((*str == 0xA0) && (*p == 0x20)) // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
+ // space character we use 0x20 space character instead to mean the same.
+ continue;
+ return 0;
+ }
+ }
+ return p;
+}
+
+wchar* Int32ToHexChars(__in wchar* p, unsigned int value, int hexBase, int digits)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(p != NULL);
+
+ while (--digits >= 0 || value != 0) {
+ unsigned char digit = static_cast<unsigned char>(value & 0xF);
+ *--p = static_cast<wchar>(digit + (digit < 10? '0': hexBase));
+ value >>= 4;
+ }
+ return p;
+}
+
+STRINGREF Int32ToDecStr(int value, int digits, STRINGREF sNegative)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ CQuickBytes buf;
+
+ if (digits < 1) digits = 1;
+
+ UINT maxDigitsLength = (digits > 15) ? digits : 15; // Since an int32 can have maximum of 10 chars as a String
+ UINT bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100;
+ int negLength = 0;
+ wchar* src = NULL;
+
+ if (value < 0) {
+ _ASSERTE(sNegative != NULL);
+ src = sNegative->GetBuffer();
+ _ASSERTE(src != NULL);
+ negLength = sNegative->GetStringLength();
+ if ((UINT) negLength > bufferLength - maxDigitsLength) {
+ bufferLength = (UINT) negLength + maxDigitsLength;
+ }
+ }
+
+ wchar *buffer = (wchar*)buf.AllocThrows(bufferLength * sizeof(WCHAR));
+ wchar* p = COMNumber::Int32ToDecChars(buffer + bufferLength, value >= 0? value: -value, digits);
+ _ASSERTE(p != NULL);
+ if (value < 0) {
+ for (int i =negLength - 1; i >= 0; i--)
+ {
+ *(--p) = *(src+i);
+ }
+ }
+
+ _ASSERTE(buffer + bufferLength - p >=0 && buffer <= p);
+ return StringObject::NewString(p, (int)(buffer + bufferLength - p));
+}
+
+STRINGREF UInt32ToDecStr(unsigned int value, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = COMNumber::Int32ToDecChars(buffer + 100, value, digits);
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+STRINGREF Int32ToHexStr(unsigned int value, int hexBase, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = Int32ToHexChars(buffer + 100, value, hexBase, digits);
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+void Int32ToNumber(int value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_IntToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_INT;
+#endif
+
+
+ wchar buffer[INT32_PRECISION+1];
+ number->precision = INT32_PRECISION;
+ if (value >= 0) {
+ number->sign = 0;
+ }
+ else {
+ number->sign = 1;
+ value = -value;
+ }
+ wchar* p = COMNumber::Int32ToDecChars(buffer + INT32_PRECISION, value, 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + INT32_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+
+}
+
+void UInt32ToNumber(unsigned int value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_UIntToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_UINT;
+#endif
+
+ wchar buffer[UINT32_PRECISION+1];
+ number->precision = UINT32_PRECISION;
+ number->sign = 0;
+ wchar* p = COMNumber::Int32ToDecChars(buffer + UINT32_PRECISION, value, 0);
+ _ASSERT(p != NULL);
+ int i = (int) (buffer + UINT32_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERT(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+}
+
+
+
+
+#define LO32(x) ((unsigned int)(x))
+#define HI32(x) ((unsigned int)(((x) & UI64(0xFFFFFFFF00000000)) >> 32))
+
+STRINGREF Int64ToDecStr(__int64 value, int digits, STRINGREF sNegative)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ CQuickBytes buf;
+
+ if (digits < 1) digits = 1;
+ int sign = HI32(value);
+
+ // digits as specified in the format string can be at most 99.
+ UINT maxDigitsLength = (digits > 20) ? digits : 20;
+ UINT bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100;
+
+ if (sign < 0) {
+ value = -value;
+ _ASSERTE(sNegative);
+ int negLength = sNegative->GetStringLength();
+ if ((UINT) negLength > bufferLength - maxDigitsLength) {
+ bufferLength = negLength + maxDigitsLength;
+ }
+ }
+
+ wchar *buffer = (wchar*)buf.AllocThrows(bufferLength * sizeof(WCHAR));
+ wchar* p = buffer + bufferLength;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9((unsigned __int64*)&value), 9);
+ _ASSERTE(p != NULL);
+ digits -= 9;
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), digits);
+ _ASSERTE(p != NULL);
+ if (sign < 0) {
+ wchar* src = sNegative->GetBuffer();
+ _ASSERTE(src != NULL);
+ for (int i =sNegative->GetStringLength() - 1; i >= 0; i--)
+ {
+ *(--p) = *(src+i);
+ }
+ }
+ return StringObject::NewString(p, (int) (buffer + bufferLength - p));
+}
+
+STRINGREF UInt64ToDecStr(unsigned __int64 value, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ if (digits < 1) digits = 1;
+ wchar* p = buffer + 100;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9(&value), 9);
+ _ASSERTE(p != NULL);
+ digits -= 9;
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), digits);
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+STRINGREF Int64ToHexStr(unsigned __int64 value, int hexBase, int digits)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[100];
+ wchar* p;
+ if (HI32(value)) {
+ Int32ToHexChars(buffer + 100, LO32(value), hexBase, 8);
+ p = Int32ToHexChars(buffer + 100 - 8, HI32(value), hexBase, digits - 8);
+ }
+ else {
+ if (digits < 1) digits = 1;
+ p = Int32ToHexChars(buffer + 100, LO32(value), hexBase, digits);
+ }
+ _ASSERTE(p != NULL && p >= buffer && p < (buffer + 100));
+ return StringObject::NewString(p, (int) (buffer + 100 - p));
+}
+
+void Int64ToNumber(__int64 value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+
+#ifndef FEATURE_BCL_FORMATTING
+ number->palNumber=PAL_Int64ToNumber(value);
+ IfNullThrow(number->palNumber);
+ number->palNumberType=PALNUMBERTYPE_INT64;
+#endif
+
+ wchar buffer[INT64_PRECISION+1];
+ number->precision = INT64_PRECISION;
+ if (value >= 0) {
+ number->sign = 0;
+ }
+ else {
+ number->sign = 1;
+ value = -value;
+ }
+ wchar* p = buffer + INT64_PRECISION;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9((unsigned __int64*)&value), 9);
+ _ASSERTE(p != NULL);
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + INT64_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+}
+
+void UInt64ToNumber(unsigned __int64 value, NUMBER* number)
+{
+ WRAPPER_NO_CONTRACT
+
+ wchar buffer[UINT64_PRECISION+1];
+ number->precision = UINT64_PRECISION;
+ number->sign = 0;
+ wchar* p = buffer + UINT64_PRECISION;
+ while (HI32(value)) {
+ p = COMNumber::Int32ToDecChars(p, Int64DivMod1E9(&value), 9);
+ _ASSERTE(p != NULL);
+ }
+ p = COMNumber::Int32ToDecChars(p, LO32(value), 0);
+ _ASSERTE(p != NULL);
+ int i = (int) (buffer + UINT64_PRECISION - p);
+ number->scale = i;
+ wchar* dst = number->digits;
+ _ASSERTE(dst != NULL);
+ while (--i >= 0) *dst++ = *p++;
+ *dst = 0;
+
+}
+
+#ifndef FEATURE_BCL_FORMATTING
+void NumberToUInt64(NUMBER * number, unsigned __int64* value)
+{
+ _ASSERTE(NULL != number);
+ _ASSERTE(NULL != value);
+
+ if (NULL != number && NULL != value) {
+ (*value) = 0;
+ int i = 0;
+ while (i < NUMBER_MAXDIGITS && i < number->precision && number->digits[i] != NULL) {
+ _ASSERTE((number->digits[i] - '0') >= 0 && (number->digits[i] - '0') <= 9);
+ *value = (10 * (*value)) + (number->digits[i] - '0');
+ i++;
+ }
+ while (i < number->scale) {
+ *value = (10 * (*value));
+ i++;
+ }
+ }
+}
+#endif
+
+
+void RoundNumber(NUMBER* number, int pos)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(number != NULL);
+
+ wchar_t* digits = GetDigitsBuffer(number);
+ int i = 0;
+ while (i < pos && digits[i] != 0) i++;
+ if (i == pos && digits[i] >= '5') {
+ while (i > 0 && digits[i - 1] == '9') i--;
+ if (i > 0) {
+ digits[i - 1]++;
+ }
+ else {
+ number->scale++;
+ digits[0] = '1';
+ i = 1;
+ }
+ }
+ else {
+ while (i > 0 && digits[i - 1] == '0') i--;
+ }
+ if (i == 0) {
+ number->scale = 0;
+ number->sign = 0;
+ }
+ digits[i] = 0;
+
+#ifndef FEATURE_BCL_FORMATTING
+//
+// The PAL stores PALNUMBER as the actual numeric type where as NUMBER is in string form;
+// Convert NUMBER back into its original type and pass it to the PAL for later use
+//
+ if (0 != number->palNumber) {
+ if (PALNUMBERTYPE_DOUBLE == number->palNumberType) {
+ // no need to round NaN or infinity double values
+ if (SCALE_NAN != ((unsigned int)number->scale) && SCALE_INF != ((unsigned int)number->scale)) {
+ double value = 0.0;
+ NumberToDouble(number, &value);
+ // make sure the rounding didn't accidently cause the good value
+ // to be turned into NaN or infinity
+ if (((FPDOUBLE*)&value)->exp != 0x7FF) {
+ number->palNumber=PAL_DoubleToNumber(value);
+ }
+ }
+ }
+ else {
+ unsigned __int64 value = 0;
+ NumberToUInt64(number, &value);
+ switch(number->palNumberType) {
+ case PALNUMBERTYPE_INT:
+ _ASSERTE((value >> 32) == 0);
+ number->palNumber=PAL_IntToNumber(((unsigned int)value) * (number->sign ? -1 : 1));
+ break;
+ case PALNUMBERTYPE_UINT:
+ _ASSERTE((value >> 32) == 0);
+ number->palNumber=PAL_UIntToNumber((unsigned int)value);
+ break;
+ case PALNUMBERTYPE_INT64:
+ _ASSERTE((value >> 63) == 0);
+ number->palNumber=PAL_Int64ToNumber(((__int64)value) * (number->sign ? -1 : 1));
+ break;
+ case PALNUMBERTYPE_UINT64:
+ number->palNumber=PAL_UInt64ToNumber(value);
+ break;
+ default:
+ CONSISTENCY_CHECK_MSGF(0, ("This palNumberType is not understood '(%d)''\n", number->palNumberType));
+ break;
+ }
+ }
+ }
+#endif
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#endif
+
+wchar ParseFormatSpecifier(STRINGREF str, int* digits)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(digits != NULL);
+
+ if (str != 0) {
+ wchar* p = str->GetBuffer();
+ _ASSERTE(p != NULL);
+ wchar ch = *p;
+ if (ch != 0) {
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
+ p++;
+ int n = -1;
+ if (*p >= '0' && *p <= '9') {
+ n = *p++ - '0';
+ while (*p >= '0' && *p <= '9') {
+ n = n * 10 + *p++ - '0';
+ if (n >= 10) break;
+ }
+ }
+ if (*p == 0) {
+ *digits = n;
+ return ch;
+ }
+ }
+ return 0;
+ }
+ }
+ *digits = -1;
+ return 'G';
+}
+
+wchar* FormatExponent(__in wchar* buffer, int value, wchar expChar,
+ STRINGREF posSignStr, STRINGREF negSignStr, int minDigits)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(buffer != NULL);
+
+ wchar digits[11];
+ *buffer++ = expChar;
+ if (value < 0) {
+ _ASSERTE(negSignStr != NULL);
+ AddStringRef(&buffer, negSignStr);
+ value = -value;
+ }
+ else {
+ if (posSignStr!= NULL) {
+ AddStringRef(&buffer, posSignStr);
+ }
+ }
+ wchar* p = COMNumber::Int32ToDecChars(digits + 10, value, minDigits);
+ _ASSERTE(p != NULL);
+ int i = (int) (digits + 10 - p);
+ while (--i >= 0) *buffer++ = *p++;
+ return buffer;
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // Go back to command line default optimizations
+#endif
+
+wchar* FormatGeneral(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, wchar expChar,
+ STRINGREF sNumberDecimal, STRINGREF sPositive, STRINGREF sNegative, STRINGREF sZero, BOOL bSuppressScientific = FALSE)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(buffer != NULL);
+
+ int digPos = number->scale;
+ int scientific = 0;
+ if (!bSuppressScientific) { // Don't switch to scientific notation
+ if (digPos > nMaxDigits || digPos < -3) {
+ digPos = 1;
+ scientific = 1;
+ }
+ }
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ WCHAR sExp[2]={expChar};
+ LPCWSTR strNumberDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strPositive(sPositive!=NULL?sPositive->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int nChars;
+
+ // nMaxDigits for Scientific are 1 more than needed
+ if (scientific)
+ nChars=PAL_FormatScientific(NULL, buffer, cchBuffer, number->palNumber,-1,nMaxDigits-1,sExp,strNumberDecimal,strPositive,strNegative,strZero);
+ else
+ nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,-1,nMaxDigits,-1, -1,-1,strNumberDecimal,NULL,strNegative,strZero);
+
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+ wchar* dig = GetDigitsBuffer(number);
+ _ASSERT(dig != NULL);
+ if (digPos > 0) {
+ do {
+ *buffer++ = *dig != 0? *dig++: '0';
+ } while (--digPos > 0);
+ }
+ else {
+ *buffer++ = '0';
+ }
+ if (*dig != 0 || digPos < 0) {
+ AddStringRef(&buffer, sNumberDecimal);
+ while (digPos < 0) {
+ *buffer++ = '0';
+ digPos++;
+ }
+ while (*dig != 0) {
+ *buffer++ = *dig++;
+ }
+ }
+ if (scientific) buffer = FormatExponent(buffer, number->scale - 1, expChar, sPositive, sNegative, 2);
+ return buffer;
+}
+
+wchar* FormatScientific(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, wchar expChar,
+ STRINGREF sNumberDecimal, STRINGREF sPositive, STRINGREF sNegative, STRINGREF sZero)
+{
+ WRAPPER_NO_CONTRACT
+ _ASSERTE(number != NULL);
+ _ASSERTE(buffer != NULL);
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ WCHAR sExp[2]={expChar};
+ LPCWSTR strNumberDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strPositive(sPositive!=NULL?sPositive->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ // nMaxDigits passed into FormatScientific are 1 more than requested
+ int nChars=PAL_FormatScientific(NULL, buffer, cchBuffer, number->palNumber,nMinDigits, nMaxDigits-1,sExp,strNumberDecimal,strPositive,strNegative,strZero);
+ if(nChars<0)
+ ThrowLastError();
+ return buffer+nChars;
+ }
+#endif
+
+
+ wchar* dig = GetDigitsBuffer(number);
+ _ASSERTE(dig != NULL);
+ *buffer++ = *dig != 0? *dig++: '0';
+ if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point
+ AddStringRef(&buffer, sNumberDecimal);
+ while (--nMaxDigits > 0) *buffer++ = *dig != 0? *dig++: '0';
+ int e = (GetDigitsBuffer(number))[0] == 0 ? 0 : number->scale - 1;
+ buffer = FormatExponent(buffer, e, expChar, sPositive, sNegative, 3);
+ _ASSERTE(buffer != NULL);
+ return buffer;
+}
+
+wchar* FormatFixed(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits,
+ I4ARRAYREF groupDigitsRef, STRINGREF sDecimal, STRINGREF sGroup, STRINGREF sNegative,STRINGREF sZero)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strDecimal(sDecimal!=NULL?sDecimal->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,-1, -1,-1,strDecimal,W(""),strNegative,strZero);
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+
+ int digPos = number->scale;
+ wchar* dig = GetDigitsBuffer(number);
+ const I4* groupDigits = NULL;
+ if (groupDigitsRef != NULL) {
+ groupDigits = groupDigitsRef->GetDirectConstPointerToNonObjectElements();
+ }
+
+ if (digPos > 0) {
+ if (groupDigits != NULL) {
+
+ int groupSizeIndex = 0; // index into the groupDigits array.
+ int groupSizeCount = groupDigits[groupSizeIndex]; // the current total of group size.
+ int groupSizeLen = groupDigitsRef->GetNumComponents(); // the length of groupDigits array.
+ int bufferSize = digPos; // the length of the result buffer string.
+ int groupSeparatorLen = sGroup->GetStringLength(); // the length of the group separator string.
+ int groupSize = 0; // the current group size.
+
+ //
+ // Find out the size of the string buffer for the result.
+ //
+ if (groupSizeLen != 0) // You can pass in 0 length arrays
+ {
+ while (digPos > groupSizeCount) {
+ groupSize = groupDigits[groupSizeIndex];
+ if (groupSize == 0) {
+ break;
+ }
+
+ bufferSize += groupSeparatorLen;
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ }
+ groupSizeCount += groupDigits[groupSizeIndex];
+ if (groupSizeCount < 0 || bufferSize < 0) {
+ COMPlusThrow(kArgumentOutOfRangeException); // if we overflow
+ }
+ }
+ if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0
+ groupSize = 0;
+ else
+ groupSize = groupDigits[0];
+ }
+
+ groupSizeIndex = 0;
+ int digitCount = 0;
+ int digStart;
+ int digLength = (int)wcslen(dig);
+ digStart = (digPos<digLength)?digPos:digLength;
+ wchar* p = buffer + bufferSize - 1;
+ for (int i = digPos - 1; i >=0; i--) {
+ *(p--) = (i<digStart)?dig[i]:'0';
+
+ if (groupSize > 0) {
+ digitCount++;
+ if (digitCount == groupSize && i != 0) {
+ for (int j = groupSeparatorLen - 1; j >=0; j--) {
+ *(p--) = sGroup->GetBuffer()[j];
+ }
+
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ groupSize = groupDigits[groupSizeIndex];
+ }
+ digitCount = 0;
+ }
+ }
+ }
+ if (p < buffer - 1) {
+ // This indicates a buffer underflow since we write in backwards.
+ DoJITFailFast();
+ }
+ buffer += bufferSize;
+ dig += digStart;
+ } else {
+ do {
+ *buffer++ = *dig != 0? *dig++: '0';
+ } while (--digPos > 0);
+ }
+ }
+ else {
+ *buffer++ = '0';
+ }
+ if (nMaxDigits > 0) {
+ AddStringRef(&buffer, sDecimal);
+ while (digPos < 0 && nMaxDigits > 0) {
+ *buffer++ = '0';
+ digPos++;
+ nMaxDigits--;
+ }
+ while (nMaxDigits > 0) {
+ *buffer++ = *dig != 0? *dig++: '0';
+ nMaxDigits--;
+ }
+ }
+ return buffer;
+}
+
+wchar* FormatNumber(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, int cNegativeNumberFormat, I4ARRAYREF cNumberGroup, STRINGREF sNumberDecimal, STRINGREF sNumberGroup, STRINGREF sNegative, STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strDecimal(sNumberDecimal!=NULL?sNumberDecimal->GetBuffer():NULL);
+ LPCWSTR strGroup(sNumberGroup!=NULL?sNumberGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+ if(cNumberGroup!=NULL)
+ {
+ int nGroups = cNumberGroup->GetNumComponents();
+ I4* pGroups=(I4*)cNumberGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatDecimal(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits, cNegativeNumberFormat,
+ iPrimaryGroup,iSecondaryGroup,strDecimal,strGroup,strNegative,strZero);
+ if(nChars<0)
+ return NULL;
+ return buffer+nChars;
+ }
+#endif
+
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negNumberFormats[cNegativeNumberFormat]:
+ posNumberFormat;
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cNumberGroup,
+ sNumberDecimal, sNumberGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+
+}
+
+wchar* FormatCurrency(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits,int nMaxDigits, int cNegCurrencyFormat, int cPosCurrencyFormat, I4ARRAYREF cCurrencyGroup,
+ STRINGREF sCurrencyDecimal, STRINGREF sCurrencyGroup, STRINGREF sNegative, STRINGREF sCurrency,STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strCurrencyDecimal(sCurrencyDecimal!=NULL?sCurrencyDecimal->GetBuffer():NULL);
+ LPCWSTR strCurrencyGroup(sCurrencyGroup!=NULL?sCurrencyGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strCurrency(sCurrency!=NULL?sCurrency->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+
+ if(cCurrencyGroup!=NULL)
+ {
+ int nGroups = cCurrencyGroup->GetNumComponents();
+ I4* pGroups=(I4*)cCurrencyGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatCurrency(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,cNegCurrencyFormat, cPosCurrencyFormat,
+ iPrimaryGroup, iSecondaryGroup, strCurrencyDecimal,strCurrencyGroup,strNegative, strCurrency,strZero);
+ if (nChars<0)
+ return NULL;
+
+ return buffer+nChars;
+ }
+#endif
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negCurrencyFormats[cNegCurrencyFormat]:
+ posCurrencyFormats[cPosCurrencyFormat];
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cCurrencyGroup,
+ sCurrencyDecimal, sCurrencyGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ case '$':
+ AddStringRef(&buffer, sCurrency);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+}
+
+wchar* FormatPercent(__in_ecount(cchBuffer) wchar* buffer, SIZE_T cchBuffer, NUMBER* number, int nMinDigits, int nMaxDigits, int cNegativePercentFormat, int cPositivePercentFormat, I4ARRAYREF cPercentGroup,
+ STRINGREF sPercentDecimal, STRINGREF sPercentGroup, STRINGREF sNegative, STRINGREF sPercent, STRINGREF sZero)
+{
+ CONTRACTL {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(buffer));
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (number->palNumber)
+ {
+ LPCWSTR strPercentDecimal(sPercentDecimal!=NULL?sPercentDecimal->GetBuffer():NULL);
+ LPCWSTR strPercentGroup(sPercentGroup!=NULL?sPercentGroup->GetBuffer():NULL);
+ LPCWSTR strNegative(sNegative!=NULL?sNegative->GetBuffer():NULL);
+ LPCWSTR strPercent(sPercent!=NULL?sPercent->GetBuffer():NULL);
+ LPCWSTR strZero(sZero!=NULL?sZero->GetBuffer():NULL);
+
+ int iPrimaryGroup=0;
+ int iSecondaryGroup=0;
+
+ if(cPercentGroup!=NULL)
+ {
+ int nGroups = cPercentGroup->GetNumComponents();
+ I4* pGroups=(I4*)cPercentGroup->GetDataPtr();
+
+ if(nGroups>0)
+ iPrimaryGroup=pGroups[0];
+ if(nGroups>1)
+ iSecondaryGroup=pGroups[1];
+ }
+
+ int nChars=PAL_FormatPercent(NULL, buffer, cchBuffer, number->palNumber,nMinDigits,nMaxDigits,cNegativePercentFormat, cPositivePercentFormat,
+ iPrimaryGroup, iSecondaryGroup,strPercentDecimal,strPercentGroup,strNegative, strPercent,strZero);
+ if(nChars<0)
+ return NULL;
+
+ return buffer+nChars;
+ }
+#endif
+
+ char ch;
+ const char* fmt;
+ fmt = number->sign?
+ negPercentFormats[cNegativePercentFormat]:
+ posPercentFormats[cPositivePercentFormat];
+
+ while ((ch = *fmt++) != 0) {
+ switch (ch) {
+ case '#':
+ buffer = FormatFixed(buffer, cchBuffer, number, nMinDigits,nMaxDigits,
+ cPercentGroup,
+ sPercentDecimal, sPercentGroup,sNegative,sZero);
+ break;
+ case '-':
+ AddStringRef(&buffer, sNegative);
+ break;
+ case '%':
+ AddStringRef(&buffer, sPercent);
+ break;
+ default:
+ *buffer++ = ch;
+ }
+ }
+ return buffer;
+}
+
+STRINGREF NumberToString(NUMBER* number, wchar format, int nMaxDigits, NUMFMTREF numfmt, BOOL bDecimal = FALSE )
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(number));
+ } CONTRACTL_END;
+
+ int nMinDigits=-1;
+ STRINGREF sZero=NULL;
+
+ // @TODO what if not sequential?
+#ifndef FEATURE_CORECLR
+ if (numfmt->iDigitSubstitution == 2) // native digits
+ {
+ PTRARRAYREF aDigits = numfmt->sNativeDigits;
+ if (aDigits!=NULL && aDigits->GetNumComponents()>0)
+ sZero=(STRINGREF)aDigits->GetAt(0);
+ }
+#endif
+
+ // Do the worst case calculation
+ /* US English - for Double.MinValue.ToString("C99"); we require 514 characters
+ ----------
+ 2 paranthesis
+ 1 currency character
+ 308 characters
+ 103 group seperators
+ 1 decimal separator
+ 99 0's
+
+ digPos + 99 + 6(slack) => digPos + 105
+ C
+ sNegative
+ sCurrencyGroup
+ sCurrencyDecimal
+ sCurrency
+ F
+ sNegative
+ sNumberDecimal
+ N
+ sNegative
+ sNumberDecimal
+ sNumberGroup
+ E
+ sNegative
+ sPositive
+ sNegative (for exponent)
+ sPositive
+ sNumberDecimal
+ G
+ sNegative
+ sPositive
+ sNegative (for exponent)
+ sPositive
+ sNumberDecimal
+ P (+2 for some spaces)
+ sNegative
+ sPercentGroup
+ sPercentDecimal
+ sPercent
+ */
+
+ _ASSERTE(numfmt != NULL);
+ UINT64 newBufferLen = MIN_BUFFER_SIZE;
+
+ CQuickBytesSpecifySize<LARGE_BUFFER_SIZE * sizeof(WCHAR)> buf;
+
+ wchar *buffer = NULL;
+ wchar* dst = NULL;
+ wchar ftype = format & 0xFFDF;
+ int digCount = 0;
+
+ switch (ftype) {
+ case 'C':
+ {
+ nMinDigits = nMaxDigits >= 0 ? nMaxDigits : numfmt->cCurrencyDecimals;
+
+ if (nMaxDigits< 0)
+ nMaxDigits = numfmt->cCurrencyDecimals;
+ if (number->scale < 0)
+ digCount = 0;
+ else if (!ClrSafeInt<INT32>::addition(number->scale, nMaxDigits, digCount))
+ COMPlusThrowOM();
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ int cNegCurrencyFormat = numfmt->cNegCurrencyFormat;
+ int cPosCurrencyFormat = numfmt->cPosCurrencyFormat;
+ I4ARRAYREF cCurrencyGroup = numfmt->cCurrencyGroup;
+ STRINGREF sCurrencyDecimal = numfmt->sCurrencyDecimal;
+ STRINGREF sCurrencyGroup = numfmt->sCurrencyGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sCurrency = numfmt->sCurrency;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatCurrency uses BCL format
+ number->palNumber = 0;
+#endif
+ // Prefix: bogus warning 22011: newBufferLen+=digCount may be smaller than MIN_BUFFER_SIZE
+ PREFIX_ASSUME(digCount >=0 && digCount <= INT32_MAX);
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sCurrencyGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sCurrencyDecimal->GetStringLength();
+ newBufferLen += sCurrency->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
+ dst = FormatCurrency(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)), number, nMinDigits,nMaxDigits, cNegCurrencyFormat, cPosCurrencyFormat, cCurrencyGroup, sCurrencyDecimal, sCurrencyGroup, sNegative, sCurrency,sZero);
+
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for ( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatCurrency(dst, nChars, number, nMinDigits, nMaxDigits, cNegCurrencyFormat, cPosCurrencyFormat, cCurrencyGroup, sCurrencyDecimal, sCurrencyGroup, sNegative, sCurrency,sZero);
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'F':
+ {
+ if (nMaxDigits< 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cNumberDecimals;
+ else
+ nMinDigits=nMaxDigits;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatFixed uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+ dst = FormatFixed(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)-(dst-buffer)), number, nMinDigits,nMaxDigits,
+ NULL,
+ sNumberDecimal, NULL, sNegative, sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatFixed(dst, nChars, number, nMinDigits,nMaxDigits,
+ NULL,
+ sNumberDecimal, NULL,sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'N':
+ {
+ if (nMaxDigits < 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cNumberDecimals; // Since we are using digits in our calculation
+ else
+ nMinDigits=nMaxDigits;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ I4ARRAYREF cNumberGroup = numfmt->cNumberGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNumberGroup = numfmt->sNumberGroup;
+ int cNegativeNumberFormat = numfmt->cNegativeNumberFormat;
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatNumber uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sNumberGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ dst = FormatNumber(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)),number, nMinDigits, nMaxDigits, cNegativeNumberFormat, cNumberGroup, sNumberDecimal, sNumberGroup, sNegative, sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatNumber(dst, nChars, number, nMinDigits,nMaxDigits, cNegativeNumberFormat, cNumberGroup, sNumberDecimal, sNumberGroup, sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'E':
+ {
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+
+ if (nMaxDigits < 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = 6;
+ else
+ nMinDigits=nMaxDigits;
+ nMaxDigits++;
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatScientific uses BCL format
+ number->palNumber = 0;
+#endif
+
+ newBufferLen += nMaxDigits;
+ newBufferLen += (((INT64)sNegative->GetStringLength() + sPositive->GetStringLength()) *2); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, nMaxDigits);
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+ dst = FormatScientific(dst, static_cast<SIZE_T>(newBufferLen * sizeof(WCHAR)-(dst-buffer)),number, nMinDigits,nMaxDigits, format, sNumberDecimal, sPositive, sNegative,sZero);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatScientific(dst, nChars, number, nMinDigits, nMaxDigits, format, sNumberDecimal, sPositive, sNegative,sZero);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ break;
+ }
+ case 'G':
+ {
+ bool enableRounding = true;
+ if (nMaxDigits < 1) {
+ if (bDecimal && (nMaxDigits == -1)) { // Default to 29 digits precision only for G formatting without a precision specifier
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = DECIMAL_PRECISION;
+ enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+ }
+ else {
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = number->precision;
+ }
+ }
+ else
+ nMinDigits=nMaxDigits;
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+ newBufferLen += nMaxDigits;
+ newBufferLen += (((INT64)sNegative->GetStringLength() + sPositive->GetStringLength()) *2); // For number and exponent
+ newBufferLen += sNumberDecimal->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ if (enableRounding) // Don't round for G formatting without precision
+ RoundNumber(number, nMaxDigits); // This also fixes up the minus zero case
+ else {
+ if (bDecimal && ((GetDigitsBuffer(number))[0] == 0)) { // Minus zero should be formatted as 0
+ number->sign = 0;
+ }
+ }
+ if (number->sign) {
+ AddStringRef(&dst, sNegative);
+ }
+
+
+#ifndef FEATURE_BCL_FORMATTING
+ if (numfmt->bIsInvariant || 0 == number->palNumber)
+ {
+ // So that FormatScientific uses BCL format
+ number->palNumber = 0;
+#endif
+
+ dst = FormatGeneral(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)), number, nMinDigits,nMaxDigits, format - ('G' - 'E'), sNumberDecimal, sPositive, sNegative, sZero, !enableRounding);
+#ifndef FEATURE_BCL_FORMATTING
+ }
+ else
+ {
+ for( SIZE_T nChars=128;;nChars*=2)
+ {
+ dst = buffer = (WCHAR*)buf.AllocThrows(nChars * sizeof(WCHAR));
+ dst = FormatGeneral(dst, nChars, number, nMinDigits,nMaxDigits, format - ('G' - 'E'), sNumberDecimal, sPositive, sNegative, sZero, !enableRounding);
+
+ if (dst)
+ break;
+ if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
+ ThrowLastError();
+ }
+ }
+#endif
+
+ }
+ break;
+ case 'P':
+ {
+ if (nMaxDigits< 0)
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = nMinDigits = numfmt->cPercentDecimals;
+ else
+ nMinDigits=nMaxDigits;
+ number->scale += 2;
+
+ if (number->scale < 0)
+ digCount = 0;
+ else
+ digCount = number->scale + nMaxDigits;
+
+
+
+ // It is critical to format with the same values that we use to calculate buffer size.
+ int cNegativePercentFormat = numfmt->cNegativePercentFormat;
+ int cPositivePercentFormat = numfmt->cPositivePercentFormat;
+ I4ARRAYREF cPercentGroup = numfmt->cPercentGroup;
+ STRINGREF sPercentDecimal = numfmt->sPercentDecimal;
+ STRINGREF sPercentGroup = numfmt->sPercentGroup;
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPercent = numfmt->sPercent;
+
+#ifndef FEATURE_BCL_FORMATTING
+ // So that FormatPercent uses BCL format
+ number->palNumber = 0;
+#endif
+ newBufferLen += digCount;
+ newBufferLen += sNegative->GetStringLength(); // For number and exponent
+ if (!ClrSafeInt<UINT64>::addition((UINT64)sPercentGroup->GetStringLength() * digCount, newBufferLen, newBufferLen))
+ COMPlusThrowOM();
+ newBufferLen += sPercentDecimal->GetStringLength();
+ newBufferLen += sPercent->GetStringLength();
+
+ _ASSERTE(newBufferLen >= MIN_BUFFER_SIZE);
+ if (newBufferLen > INT32_MAX) {
+ COMPlusThrowOM();
+ }
+ newBufferLen = newBufferLen * sizeof(WCHAR);
+ dst = buffer = (WCHAR*)buf.AllocThrows(static_cast<SIZE_T>(newBufferLen));
+
+ RoundNumber(number, number->scale + nMaxDigits);
+ dst = FormatPercent(dst, static_cast<SIZE_T>(newBufferLen/sizeof(WCHAR)),number, nMinDigits,nMaxDigits, cNegativePercentFormat, cPositivePercentFormat, cPercentGroup, sPercentDecimal, sPercentGroup, sNegative, sPercent, sZero);
+
+ break;
+ }
+ default:
+ COMPlusThrow(kFormatException, W("Argument_BadFormatSpecifier"));
+ }
+ // check for overflow of the preallocated buffer
+#ifdef FEATURE_BCL_FORMATTING // when not defined the buffer could be resized, so skip the check
+// Review signed/unsigned mismatch in '<=' comparison.
+#pragma warning(push)
+#pragma warning(disable:4018)
+ if (!((dst - buffer >= 0) && (dst - buffer) <= (newBufferLen / sizeof(WCHAR) ))) {
+#pragma warning(pop)
+ DoJITFailFast();
+ }
+#endif
+
+ return StringObject::NewString(buffer, (int) (dst - buffer));
+}
+
+LPCWSTR FindSection(LPCWSTR format, int section)
+{
+ LIMITED_METHOD_CONTRACT
+ _ASSERTE(format != NULL);
+
+ LPCWSTR src;
+ wchar ch;
+ if (section == 0) return format;
+ src = format;
+ for (;;) {
+ switch (ch = *src++) {
+ case '\'':
+ case '"':
+ while (*src != 0 && *src++ != ch);
+ break;
+ case '\\':
+ if (*src != 0) src++;
+ break;
+ case ';':
+ if (--section != 0) break;
+ if (*src != 0 && *src != ';') return src;
+ case 0:
+ return format;
+ }
+ }
+}
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+STRINGREF NumberToStringFormat(NUMBER* number, STRINGREF str, NUMFMTREF numfmt)
+{
+ CONTRACTL {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ int digitCount;
+ int decimalPos;
+ int firstDigit;
+ int lastDigit;
+ int digPos;
+ int scientific;
+ int percent;
+ int permille;
+ int thousandPos;
+ int thousandCount = 0;
+ int thousandSeps;
+ int scaleAdjust;
+ int adjust;
+ wchar* format=NULL;
+ LPCWSTR section=NULL;
+ LPCWSTR src=NULL;
+ wchar* dst=NULL;
+ wchar* dig=NULL;
+ wchar ch;
+ wchar* buffer=NULL;
+ CQuickBytes buf;
+
+ _ASSERTE(str != NULL);
+ _ASSERTE(numfmt != NULL);
+
+ STRINGREF sNegative = numfmt->sNegative;
+ STRINGREF sPositive = numfmt->sPositive;
+ STRINGREF sNumberDecimal = numfmt->sNumberDecimal;
+ STRINGREF sPercent = numfmt->sPercent;
+ STRINGREF sPerMille = numfmt->sPerMille;
+ STRINGREF sNumberGroup = numfmt->sNumberGroup;
+ I4ARRAYREF cNumberGroup = numfmt->cNumberGroup;
+
+ format = str->GetBuffer();
+
+ section = FindSection(format, (GetDigitsBuffer(number))[0] == 0 ? 2 : number->sign ? 1 : 0);
+
+#ifndef FEATURE_BCL_FORMATTING
+ // custom formatting is all done in the VM without the PAL. Blanking
+ // the palNumber field avoids unnecessary RoundNumber calculations
+ number->palNumber = 0;
+#endif
+
+ParseSection:
+ digitCount = 0;
+ decimalPos = -1;
+ firstDigit = 0x7FFFFFFF;
+ lastDigit = 0;
+ scientific = 0;
+ percent = 0;
+ permille = 0;
+ thousandPos = -1;
+ thousandSeps = 0;
+ scaleAdjust = 0;
+ src = section;
+ _ASSERTE(src != NULL);
+ while ((ch = *src++) != 0 && ch != ';') {
+ switch (ch) {
+ case '#':
+ digitCount++;
+ break;
+ case '0':
+ if (firstDigit == 0x7FFFFFFF) firstDigit = digitCount;
+ digitCount++;
+ lastDigit = digitCount;
+ break;
+ case '.':
+ if (decimalPos < 0) {
+ decimalPos = digitCount;
+ }
+ break;
+ case ',':
+ if (digitCount > 0 && decimalPos < 0) {
+ if (thousandPos >= 0) {
+ if (thousandPos == digitCount) {
+ thousandCount++;
+ break;
+ }
+ thousandSeps = 1;
+ }
+ thousandPos = digitCount;
+ thousandCount = 1;
+ }
+ break;
+ case '%':
+ percent++;
+ scaleAdjust += 2;
+ break;
+ case 0x2030:
+ permille++;
+ scaleAdjust += 3;
+ break;
+ case '\'':
+ case '"':
+ while (*src != 0 && *src++ != ch);
+ break;
+ case '\\':
+ if (*src != 0) src++;
+ break;
+ case 'E':
+ case 'e':
+ if (*src=='0' || ((*src == '+' || *src == '-') && src[1] == '0')) {
+ while (*++src == '0');
+ scientific = 1;
+ }
+ break;
+ }
+ }
+
+ if (decimalPos < 0) decimalPos = digitCount;
+ if (thousandPos >= 0) {
+ if (thousandPos == decimalPos) {
+ scaleAdjust -= thousandCount * 3;
+ }
+ else {
+ thousandSeps = 1;
+ }
+ }
+ if ((GetDigitsBuffer(number))[0] != 0) {
+ number->scale += scaleAdjust;
+ int pos = scientific? digitCount: number->scale + digitCount - decimalPos;
+ RoundNumber(number, pos);
+ if ((GetDigitsBuffer(number))[0] == 0) {
+ src = FindSection(format, 2);
+ if (src != section) {
+ section = src;
+ goto ParseSection;
+ }
+ }
+ } else {
+ number->sign = 0; // We need to format -0 without the sign set.
+ number->scale = 0; // Decimals with scale ('0.00') should be rounded.
+ }
+
+ firstDigit = firstDigit < decimalPos? decimalPos - firstDigit: 0;
+ lastDigit = lastDigit > decimalPos? decimalPos - lastDigit: 0;
+ if (scientific) {
+ digPos = decimalPos;
+ adjust = 0;
+ }
+ else {
+ digPos = number->scale > decimalPos? number->scale: decimalPos;
+ adjust = number->scale - decimalPos;
+ }
+ src = section;
+ dig = GetDigitsBuffer(number);
+
+ // Find maximum number of characters that the destination string can grow by
+ // in the following while loop. Use this to avoid buffer overflows.
+ // Longest strings are potentially +/- signs with 10 digit exponents,
+ // or decimal numbers, or the while loops copying from a quote or a \ onwards.
+ // Check for positive and negative
+ UINT64 maxStrIncLen = 0; // We need this to be UINT64 since the percent computation could go beyond a UINT.
+ if (number->sign) {
+ maxStrIncLen = sNegative->GetStringLength();
+ }
+ else {
+ maxStrIncLen = sPositive->GetStringLength();
+ }
+
+ // Add for any big decimal seperator
+ maxStrIncLen += sNumberDecimal->GetStringLength();
+
+ // Add for scientific
+ if (scientific) {
+ int inc1 = sPositive->GetStringLength();
+ int inc2 = sNegative->GetStringLength();
+ maxStrIncLen +=(inc1>inc2)?inc1:inc2;
+ }
+
+ // Add for percent separator
+ if (percent) {
+ maxStrIncLen += ((INT64)sPercent->GetStringLength()) * percent;
+ }
+
+ // Add for permilli separator
+ if (permille) {
+ maxStrIncLen += ((INT64)sPerMille->GetStringLength()) * permille;
+ }
+
+ //adjust can be negative, so we make this an int instead of an unsigned int.
+ // adjust represents the number of characters over the formatting eg. format string is "0000" and you are trying to
+ // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be
+ // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example.
+ INT64 adjustLen=(adjust>0)?adjust:0; // We need to add space for these extra characters anyway.
+ CQuickBytes thousands;
+ INT32 bufferLen2 = 125;
+ INT32 *thousandsSepPos = NULL;
+ INT32 thousandsSepCtr = -1;
+
+ if (thousandSeps) { // Fixup possible buffer overrun problems
+ // We need to precompute this outside the number formatting loop
+ if(cNumberGroup->GetNumComponents() == 0) {
+ thousandSeps = 0; // Nothing to add
+ }
+ else {
+ thousandsSepPos = (INT32 *)thousands.AllocThrows(bufferLen2 * sizeof(INT32));
+ // We need this array to figure out where to insert the thousands separator. We would have to traverse the string
+ // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert
+ // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos.
+ // The max is not bound since you can have formatting strings of the form "000,000..", and this
+ // should handle that case too.
+
+ const I4* groupDigits = cNumberGroup->GetDirectConstPointerToNonObjectElements();
+ _ASSERTE(groupDigits != NULL);
+
+ int groupSizeIndex = 0; // index into the groupDigits array.
+ INT64 groupTotalSizeCount = 0;
+ int groupSizeLen = cNumberGroup->GetNumComponents(); // the length of groupDigits array.
+ if (groupSizeLen != 0)
+ groupTotalSizeCount = groupDigits[groupSizeIndex]; // the current running total of group size.
+ int groupSize = static_cast<INT32>(groupTotalSizeCount); // safe cast as long as groupDigits remains I4
+
+ int totalDigits = digPos + ((adjust < 0)?adjust:0); // actual number of digits in o/p
+ int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits;
+ while (numDigits > groupTotalSizeCount) {
+ if (groupSize == 0)
+ break;
+ thousandsSepPos[++thousandsSepCtr] = static_cast<INT32>(groupTotalSizeCount);
+ if (groupSizeIndex < groupSizeLen - 1) {
+ groupSizeIndex++;
+ groupSize = groupDigits[groupSizeIndex];
+ }
+ groupTotalSizeCount += groupSize;
+ if (bufferLen2 - thousandsSepCtr < 10) { // Slack of 10
+ bufferLen2 *= 2;
+ thousands.ReSizeThrows(bufferLen2*sizeof(INT32)); // memcopied by CQuickBytes automatically
+ thousandsSepPos = (INT32 *)thousands.Ptr();
+ }
+ }
+
+ // We already have computed the number of separators above. Simply add space for them.
+ adjustLen += ( (thousandsSepCtr + 1) * ((INT64)sNumberGroup->GetStringLength()));
+ }
+ }
+
+ maxStrIncLen += adjustLen;
+
+ // Allocate temp buffer - gotta deal with Schertz' 500 MB strings.
+ // Some computations like when you specify Int32.MaxValue-2 %'s and each percent is setup to be Int32.MaxValue in length
+ // will generate a result that will be largest than an unsigned int can hold. This is to protect against overflow.
+ UINT64 tempLen = str->GetStringLength() + maxStrIncLen + 10; // Include a healthy amount of temp space.
+ if (tempLen > 0x7FFFFFFF)
+ COMPlusThrowOM(); // if we overflow
+
+ unsigned int bufferLen = (UINT)tempLen;
+ if (bufferLen < 250) // Stay under 512 bytes
+ bufferLen = 250; // This is to prevent unnecessary calls to resize
+ buffer = (wchar *) buf.AllocThrows(bufferLen* sizeof(WCHAR));
+ dst = buffer;
+
+
+ if (number->sign && section == format) {
+ AddStringRef(&dst, sNegative);
+ }
+
+ BOOL decimalWritten = FALSE;
+
+ while ((ch = *src++) != 0 && ch != ';') {
+ // Make sure temp buffer is big enough, else resize it.
+ if (bufferLen - (unsigned int)(dst-buffer) < 10) {
+ int offset = static_cast<INT32>(dst - buffer);
+ bufferLen *= 2;
+ buf.ReSizeThrows(bufferLen*sizeof(WCHAR));
+ buffer = (wchar*)buf.Ptr(); // memcopied by QuickBytes automatically
+ dst = buffer + offset;
+ }
+
+ if (adjust > 0) {
+ switch (ch) {
+ case '#':
+ case '0':
+ case '.':
+ while (adjust > 0) { // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at
+ // the character after which the groupSeparator needs to be appended.
+ *dst++ = *dig != 0? *dig++: '0';
+ if (thousandSeps && digPos > 1 && thousandsSepCtr>=0) {
+ if (digPos == thousandsSepPos[thousandsSepCtr] + 1) {
+ AddStringRef(&dst, sNumberGroup);
+ thousandsSepCtr--;
+ }
+ }
+ digPos--;
+ adjust--;
+ }
+ }
+ }
+
+ switch (ch) {
+ case '#':
+ case '0':
+ {
+ if (adjust < 0) {
+ adjust++;
+ ch = digPos <= firstDigit? '0': 0;
+ }
+ else {
+ ch = *dig != 0? *dig++: digPos > lastDigit? '0': 0;
+ }
+ if (ch != 0) {
+ *dst++ = ch;
+ if (thousandSeps && digPos > 1 && thousandsSepCtr>=0) {
+ if (digPos == thousandsSepPos[thousandsSepCtr] + 1) {
+ AddStringRef(&dst, sNumberGroup);
+ thousandsSepCtr--;
+ }
+ }
+ }
+
+ digPos--;
+ break;
+ }
+ case '.':
+ {
+ if (digPos != 0 || decimalWritten) {
+ // For compatibility, don't echo repeated decimals
+ break;
+ }
+ // If the format has trailing zeros or the format has a decimal and digits remain
+ if (lastDigit < 0
+ || (decimalPos < digitCount && *dig != 0)) {
+ AddStringRef(&dst, sNumberDecimal);
+ decimalWritten = TRUE;
+ }
+ break;
+ }
+ case 0x2030:
+ AddStringRef(&dst, sPerMille);
+ break;
+ case '%':
+ AddStringRef(&dst, sPercent);
+ break;
+ case ',':
+ break;
+ case '\'':
+ case '"':
+ // Buffer overflow possibility
+ while (*src != 0 && *src != ch) {
+ *dst++ = *src++;
+ if ((unsigned int)(dst-buffer) == bufferLen-1) {
+ if (bufferLen - (unsigned int)(dst-buffer) < maxStrIncLen) {
+ int offset = static_cast<INT32>(dst - buffer);
+ bufferLen *= 2;
+ buf.ReSizeThrows(bufferLen*sizeof(WCHAR)); // memcopied by CQuickBytes automatically
+ buffer = (wchar *)buf.Ptr();
+ dst = buffer + offset;
+ }
+ }
+ }
+ if (*src != 0) src++;
+ break;
+ case '\\':
+ if (*src != 0) *dst++ = *src++;
+ break;
+ case 'E':
+ case 'e':
+ {
+ STRINGREF sign = NULL;
+ int i = 0;
+ if (scientific) {
+ if (*src=='0') {
+ //Handles E0, which should format the same as E-0
+ i++;
+ } else if (*src == '+' && src[1] == '0') {
+ //Handles E+0
+ sign = sPositive;
+ } else if (*src == '-' && src[1] == '0') {
+ //Handles E-0
+ //Do nothing, this is just a place holder s.t. we don't break out of the loop.
+ } else {
+ *dst++ = ch;
+ break;
+ }
+ while (*++src == '0') i++;
+ if (i > 10) i = 10;
+ int exp = (GetDigitsBuffer(number))[0] == 0 ? 0 : number->scale - decimalPos;
+ dst = FormatExponent(dst, exp, ch, sign, sNegative, i);
+ scientific = 0;
+ }
+ else
+ {
+ *dst++ = ch; // Copy E or e to output
+ if (*src== '+' || *src == '-') {
+ *dst++ = *src++;
+ }
+ while (*src == '0') {
+ *dst++ = *src++;
+ }
+ }
+ break;
+ }
+ default:
+ *dst++ = ch;
+ }
+ }
+ if (!((dst - buffer >= 0) && (dst - buffer <= (int)bufferLen))) {
+ DoJITFailFast();
+ }
+ STRINGREF newStr = StringObject::NewString(buffer, (int)(dst - buffer));
+ return newStr;
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+FCIMPL3_VII(Object*, COMNumber::FormatDecimal, FC_DECIMAL value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+
+ wchar fmt;
+ int digits;
+
+ STRINGREF refRetVal = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+
+ if (gc.numfmt == 0)
+ COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+
+ COMDecimal::DecimalToNumber(&value, &number);
+
+ fmt = ParseFormatSpecifier(gc.format, &digits);
+ if (fmt != 0) {
+ refRetVal = NumberToString(&number, fmt, digits, gc.numfmt, TRUE);
+ } else {
+ refRetVal = NumberToStringFormat(&number, gc.format, gc.numfmt);
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatDouble, double value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+ int digits;
+ double dTest;
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ STRINGREF refRetVal;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+ gc.refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.numfmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ wchar fmt = ParseFormatSpecifier(gc.format, &digits);
+ wchar val = (fmt & 0xFFDF);
+ int precision = DOUBLE_PRECISION;
+ switch (val) {
+ case 'R':
+ //In order to give numbers that are both friendly to display and round-trippable,
+ //we parse the number using 15 digits and then determine if it round trips to the same
+ //value. If it does, we convert that NUMBER to a string, otherwise we reparse using 17 digits
+ //and display that.
+
+ DoubleToNumber(value, DOUBLE_PRECISION, &number);
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ NumberToDouble(&number, &dTest);
+
+ if (dTest == value) {
+ gc.refRetVal = NumberToString(&number, 'G', DOUBLE_PRECISION, gc.numfmt);
+ goto lExit;
+ }
+
+ DoubleToNumber(value, 17, &number);
+ gc.refRetVal = NumberToString(&number, 'G', 17, gc.numfmt);
+ goto lExit;
+ break;
+
+ case 'E':
+ // Here we round values less than E14 to 15 digits
+ if (digits > 14) {
+ precision = 17;
+ }
+ break;
+
+ case 'G':
+ // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched
+ if (digits > 15) {
+ precision = 17;
+ }
+ break;
+
+ }
+
+ DoubleToNumber(value, precision, &number);
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ if (fmt != 0) {
+ gc.refRetVal = NumberToString( &number, fmt, digits, gc.numfmt);
+ }
+ else {
+ gc.refRetVal = NumberToStringFormat( &number, gc.format, gc.numfmt);
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetVal);
+}
+FCIMPLEND
+
+//
+//This function and the function pointer which we use to access are
+//to prevent VC7 from optimizing away our cast from double to float.
+//We need this narrowing operation to verify whether or not we successfully round-tripped
+//the single value.
+
+//
+// We need this method to have volatile arguments.
+//
+static void CvtToFloat(double val, RAW_KEYWORD(volatile) float* fltPtr)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ *fltPtr = (float)val;
+}
+
+void (*CvtToFloatPtr)(double val, RAW_KEYWORD(volatile) float* fltPtr) = CvtToFloat;
+
+
+FCIMPL3_VII(Object*, COMNumber::FormatSingle, float value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ NUMBER number;
+ int digits;
+ double dTest;
+ double argsValue = value;
+
+ struct _gc
+ {
+ STRINGREF format;
+ NUMFMTREF numfmt;
+ STRINGREF refRetVal;
+ } gc;
+
+ gc.format = (STRINGREF) formatUNSAFE;
+ gc.numfmt = (NUMFMTREF) numfmtUNSAFE;
+ gc.refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.numfmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ wchar fmt = ParseFormatSpecifier(gc.format, &digits);
+ wchar val = fmt & 0xFFDF;
+ int precision = FLOAT_PRECISION;
+ switch (val) {
+ case 'R':
+ {
+ //In order to give numbers that are both friendly to display and round-trippable,
+ //we parse the number using 7 digits and then determine if it round trips to the same
+ //value. If it does, we convert that NUMBER to a string, otherwise we reparse using 9 digits
+ //and display that.
+
+ DoubleToNumber(argsValue, FLOAT_PRECISION, &number);
+#ifndef FEATURE_BCL_FORMATTING
+ // Make sure that BCL formatting is used for Single to avoid lossy conversion to Double
+ number.palNumber = 0;
+#endif
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ NumberToDouble(&number, &dTest);
+
+ Volatile<float> fTest;
+
+ (*CvtToFloatPtr)(dTest, &fTest);
+
+ if (fTest == value) {
+ gc.refRetVal = NumberToString(&number, 'G', FLOAT_PRECISION, gc.numfmt);
+ goto lExit;
+ }
+
+ DoubleToNumber(argsValue, 9, &number);
+ gc.refRetVal = NumberToString(&number, 'G', 9, gc.numfmt);
+ goto lExit;
+ }
+ break;
+ case 'E':
+ // Here we round values less than E14 to 15 digits
+ if (digits > 6) {
+ precision = 9;
+ }
+ break;
+
+
+ case 'G':
+ // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched
+ if (digits > 7) {
+ precision = 9;
+ }
+ break;
+ }
+
+ DoubleToNumber(value, precision, &number);
+#ifndef FEATURE_BCL_FORMATTING
+ // Make sure that BCL formatting is used for Single to avoid lossy conversion to Double
+ number.palNumber = 0;
+#endif
+
+ if (number.scale == (int) SCALE_NAN) {
+ gc.refRetVal = gc.numfmt->sNaN;
+ goto lExit;
+ }
+
+ if (number.scale == SCALE_INF) {
+ gc.refRetVal = (number.sign? gc.numfmt->sNegativeInfinity: gc.numfmt->sPositiveInfinity);
+ goto lExit;
+ }
+
+ if (fmt != 0) {
+ gc.refRetVal = NumberToString( &number, fmt, digits, gc.numfmt);
+ }
+ else {
+ gc.refRetVal = NumberToStringFormat( &number, gc.format, gc.numfmt);
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetVal);
+}
+FCIMPLEND
+
+FCIMPL3(Object*, COMNumber::FormatInt32, INT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = (STRINGREF)formatUNSAFE;
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+
+ //ANDing fmt with FFDF has the effect of uppercasing the character because
+ //we've removed the bit that marks lower-case.
+ switch (fmt & 0xFFDF) {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ Int32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = Int32ToDecStr(value, digits, gc.refNumFmt->sNegative);
+ break;
+ case 'X':
+ //The fmt-(X-A+10) has the effect of dictating whether we produce uppercase
+ //or lowercase hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x'
+ //as the format code produces lowercase.
+ gc.refRetString = Int32ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ Int32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3(Object*, COMNumber::FormatUInt32, UINT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = (STRINGREF)formatUNSAFE;
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF)
+ {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ UInt32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = UInt32ToDecStr(value, digits);
+ break;
+ case 'X':
+ gc.refRetString = Int32ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ UInt32ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatInt64, INT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF)
+ {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ Int64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = Int64ToDecStr(value, digits, gc.refNumFmt->sNegative);
+ break;
+ case 'X':
+ gc.refRetString = Int64ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ Int64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+FCIMPL3_VII(Object*, COMNumber::FormatUInt64, UINT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ switch (fmt & 0xFFDF) {
+ case 'G':
+ if (digits > 0)
+ {
+ NUMBER number;
+ UInt64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+ // fall through
+ case 'D':
+ gc.refRetString = UInt64ToDecStr(value, digits);
+ break;
+ case 'X':
+ gc.refRetString = Int64ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ break;
+ default:
+ NUMBER number;
+ UInt64ToNumber(value, &number);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(&number, fmt, digits, gc.refNumFmt);
+ break;
+ }
+ gc.refRetString = NumberToStringFormat(&number, gc.refFormat, gc.refNumFmt);
+ break;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+#if !defined(FEATURE_CORECLR)
+//
+// Used by base types that are not in mscorlib.dll (such as System.Numerics.BigInteger in System.Core.dll)
+// Note that the allDigits buffer must be fixed across this call or you will introduce a GC Hole.
+//
+FCIMPL4(Object*, COMNumber::FormatNumberBuffer, BYTE* number, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE, __in_z wchar_t* allDigits)
+{
+ FCALL_CONTRACT;
+
+ wchar fmt;
+ int digits;
+ NUMBER* pNumber;
+
+ struct _gc
+ {
+ STRINGREF refFormat;
+ NUMFMTREF refNumFmt;
+ STRINGREF refRetString;
+ } gc;
+
+ gc.refFormat = ObjectToSTRINGREF(formatUNSAFE);
+ gc.refNumFmt = (NUMFMTREF)numfmtUNSAFE;
+ gc.refRetString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ if (gc.refNumFmt == 0) COMPlusThrowArgumentNull(W("NumberFormatInfo"));
+
+ pNumber = (NUMBER*) number;
+
+ pNumber->allDigits = allDigits;
+
+ fmt = ParseFormatSpecifier(gc.refFormat, &digits);
+ if (fmt != 0) {
+ gc.refRetString = NumberToString(pNumber, fmt, digits, gc.refNumFmt);
+ }
+ else {
+ gc.refRetString = NumberToStringFormat(pNumber, gc.refFormat, gc.refNumFmt);
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.refRetString);
+}
+FCIMPLEND
+
+#endif // !FEATURE_CORECLR
+
+FCIMPL2(FC_BOOL_RET, COMNumber::NumberBufferToDecimal, BYTE* number, DECIMAL* value)
+{
+ FCALL_CONTRACT;
+
+ FC_RETURN_BOOL(COMDecimal::NumberToDecimal((NUMBER *) number, value) != 0);
+}
+FCIMPLEND
+
+FCIMPL2(FC_BOOL_RET, COMNumber::NumberBufferToDouble, BYTE* number, double* value)
+{
+ FCALL_CONTRACT;
+
+ double d = 0;
+ NumberToDouble((NUMBER*) number, &d);
+ unsigned int e = ((FPDOUBLE*)&d)->exp;
+ unsigned int fmntLow = ((FPDOUBLE*)&d)->mantLo;
+ unsigned int fmntHigh = ((FPDOUBLE*)&d)->mantHi;
+ if (e == 0x7FF) {
+ FC_RETURN_BOOL(false);
+ }
+ if (e == 0 && fmntLow ==0 && fmntHigh == 0) {
+ d = 0;
+ }
+ *value = d;
+ FC_RETURN_BOOL(true);
+}
+FCIMPLEND
diff --git a/src/classlibnative/bcltype/number.h b/src/classlibnative/bcltype/number.h
new file mode 100644
index 0000000000..3b72781a18
--- /dev/null
+++ b/src/classlibnative/bcltype/number.h
@@ -0,0 +1,61 @@
+// 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.
+//
+// File: Number.h
+//
+
+//
+
+#ifndef _NUMBER_H_
+#define _NUMBER_H_
+
+#include <pshpack1.h>
+
+#define NUMBER_MAXDIGITS 50
+
+#ifndef FEATURE_BCL_FORMATTING
+enum PAL_NUMBERType {
+ PALNUMBERTYPE_INT = 1, // PAL_IntToNumber
+ PALNUMBERTYPE_INT64 = 2, // PAL_Int64ToNumber
+ PALNUMBERTYPE_UINT = 3, // PAL_UIntToNumber
+ PALNUMBERTYPE_UINT64 = 4, // PAL_UInt64ToNumber
+ PALNUMBERTYPE_DOUBLE = 5, // PAL_DoubleToNumber
+};
+#endif
+
+struct NUMBER {
+ int precision;
+ int scale;
+ int sign;
+ wchar_t digits[NUMBER_MAXDIGITS + 1];
+ wchar_t* allDigits;
+#ifndef FEATURE_BCL_FORMATTING
+ PAL_NUMBERHolder palNumber;
+ PAL_NUMBERType palNumberType;
+#endif
+ NUMBER() : precision(0), scale(0), sign(0), allDigits(NULL) {}
+};
+
+class COMNumber
+{
+public:
+ static FCDECL3_VII(Object*, FormatDecimal, FC_DECIMAL value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3_VII(Object*, FormatDouble, double value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3_VII(Object*, FormatSingle, float value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3(Object*, FormatInt32, INT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3(Object*, FormatUInt32, UINT32 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3_VII(Object*, FormatInt64, INT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+ static FCDECL3_VII(Object*, FormatUInt64, UINT64 value, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE);
+#if !defined(FEATURE_CORECLR)
+ static FCDECL4(Object*, FormatNumberBuffer, BYTE* number, StringObject* formatUNSAFE, NumberFormatInfo* numfmtUNSAFE, __in_z wchar_t* allDigits);
+#endif // !FEATURE_CORECLR
+ static FCDECL2(FC_BOOL_RET, NumberBufferToDecimal, BYTE* number, DECIMAL* value);
+ static FCDECL2(FC_BOOL_RET, NumberBufferToDouble, BYTE* number, double* value);
+
+ static wchar_t* Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits);
+};
+
+#include <poppack.h>
+
+#endif // _NUMBER_H_
diff --git a/src/classlibnative/bcltype/oavariant.cpp b/src/classlibnative/bcltype/oavariant.cpp
new file mode 100644
index 0000000000..e7f92a7a33
--- /dev/null
+++ b/src/classlibnative/bcltype/oavariant.cpp
@@ -0,0 +1,430 @@
+// 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.
+//
+// File: OAVariant.cpp
+//
+
+//
+// Purpose: Wrapper for Ole Automation compatable math ops.
+// Calls through to OleAut.dll
+//
+
+//
+
+#include <common.h>
+
+#ifdef FEATURE_COMINTEROP
+
+#include <oleauto.h>
+#include "excep.h"
+#include "oavariant.h"
+#include "comdatetime.h" // DateTime <-> OleAut date conversions
+#include "interoputil.h"
+#include "interopconverter.h"
+#include "excep.h"
+#include "string.h"
+#include "comutilnative.h" // for COMDate
+
+#define INVALID_MAPPING (BYTE)(-1)
+
+static const BYTE CVtoVTTable [] =
+{
+ VT_EMPTY, // CV_EMPTY
+ VT_VOID, // CV_VOID
+ VT_BOOL, // CV_BOOLEAN
+ VT_UI2, // CV_CHAR
+ VT_I1, // CV_I1
+ VT_UI1, // CV_U1
+ VT_I2, // CV_I2
+ VT_UI2, // CV_U2
+ VT_I4, // CV_I4
+ VT_UI4, // CV_U4
+ VT_I8, // CV_I8
+ VT_UI8, // CV_U8
+ VT_R4, // CV_R4
+ VT_R8, // CV_R8
+ VT_BSTR, // CV_STRING
+ INVALID_MAPPING, // CV_PTR
+ VT_DATE, // CV_DATETIME
+ INVALID_MAPPING, // CV_TIMESPAN
+ VT_UNKNOWN, // CV_OBJECT
+ VT_DECIMAL, // CV_DECIMAL
+ VT_CY, // CV_CURRENCY
+ INVALID_MAPPING, // CV_ENUM
+ INVALID_MAPPING, // CV_MISSING
+ VT_NULL, // CV_NULL
+ INVALID_MAPPING // CV_LAST
+};
+
+static const BYTE VTtoCVTable[] =
+{
+ CV_EMPTY, // VT_EMPTY
+ CV_NULL, // VT_NULL
+ CV_I2, // VT_I2
+ CV_I4, // VT_I4
+ CV_R4, // VT_R4
+ CV_R8, // VT_R8
+ CV_CURRENCY,// VT_CY
+ CV_DATETIME,// VT_DATE
+ CV_STRING, // VT_BSTR
+ INVALID_MAPPING, // VT_DISPATCH
+ INVALID_MAPPING, // VT_ERROR
+ CV_BOOLEAN, // VT_BOOL
+ CV_OBJECT, // VT_VARIANT
+ CV_OBJECT, // VT_UNKNOWN
+ CV_DECIMAL, // VT_DECIMAL
+ INVALID_MAPPING, // An unused enum table entry
+ CV_I1, // VT_I1
+ CV_U1, // VT_UI1
+ CV_U2, // VT_UI2
+ CV_U4, // VT_UI4
+ CV_I8, // VT_I8
+ CV_U8, // VT_UI8
+ CV_I4, // VT_INT
+ CV_U4, // VT_UINT
+ CV_VOID // VT_VOID
+};
+
+// Need translations from CVType to VARENUM and vice versa. CVTypes
+// is defined in COMVariant.h. VARENUM is defined in OleAut's variant.h
+// Assumption here is we will only deal with VARIANTs and not other OLE
+// constructs such as property sets or safe arrays.
+VARENUM COMOAVariant::CVtoVT(const CVTypes cv)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(cv >= 0 && cv < CV_LAST);
+ }
+ CONTRACTL_END;
+
+ if (CVtoVTTable[cv] == INVALID_MAPPING)
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType"));
+
+ return (VARENUM) CVtoVTTable[cv];
+}
+
+// Need translations from CVType to VARENUM and vice versa. CVTypes
+// is defined in COMVariant.h. VARENUM is defined in OleAut's variant.h
+CVTypes COMOAVariant::VTtoCV(const VARENUM vt)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(vt < VT_VOID);
+ }
+ CONTRACTL_END;
+
+ if (vt <0 || vt > VT_VOID || VTtoCVTable[vt]==INVALID_MAPPING)
+ COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType"));
+
+ return (CVTypes) VTtoCVTable[vt];
+}
+
+
+// Converts a COM+ Variant to an OleAut Variant. Returns true if
+// there was a native object allocated by this method that must be freed,
+// else false.
+bool COMOAVariant::ToOAVariant(const VariantData * const var, VARIANT * oa)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(var));
+ PRECONDITION(CheckPointer(oa));
+ }
+ CONTRACTL_END;
+
+ SafeVariantInit(oa);
+ UINT64 * dest = (UINT64*) &V_UI1(oa);
+ *dest = 0;
+
+ WCHAR * chars;
+ int strLen;
+
+ // Set the data field of the OA Variant to be either the object reference
+ // or the data (ie int) that it needs.
+
+ switch (var->GetType())
+ {
+ case CV_STRING:
+ if (var->GetObjRef() == NULL)
+ {
+ V_BSTR(oa) = NULL;
+ V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType()));
+
+ // OA perf feature: VarClear calls SysFreeString(null), which access violates.
+ return false;
+ }
+
+ ((STRINGREF) (var->GetObjRef()))->RefInterpretGetStringValuesDangerousForGC(&chars, &strLen);
+ V_BSTR(oa) = SysAllocStringLen(chars, strLen);
+ if (V_BSTR(oa) == NULL)
+ COMPlusThrowOM();
+
+ V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType()));
+
+ return true;
+
+ case CV_CHAR:
+ chars = (WCHAR*) var->GetData();
+ V_BSTR(oa) = SysAllocStringLen(chars, 1);
+ if (V_BSTR(oa) == NULL)
+ COMPlusThrowOM();
+
+ // We should override the VTtoVT default of VT_UI2 for this case.
+ V_VT(oa) = VT_BSTR;
+
+ return true;
+
+ case CV_DATETIME:
+ V_DATE(oa) = COMDateTime::TicksToDoubleDate(var->GetDataAsInt64());
+ V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType()));
+ return false;
+
+ case CV_BOOLEAN:
+ V_BOOL(oa) = (var->GetDataAsInt64()==0 ? VARIANT_FALSE : VARIANT_TRUE);
+ V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType()));
+ return false;
+
+ case CV_DECIMAL:
+ {
+ OBJECTREF obj = var->GetObjRef();
+ DECIMAL * d = (DECIMAL*) obj->GetData();
+ // DECIMALs and Variants are the same size. Variants are a union between
+ // all the normal Variant fields (vt, bval, etc) and a Decimal. Decimals
+ // also have the first 2 bytes reserved, for a VT field.
+
+ V_DECIMAL(oa) = *d;
+ V_VT(oa) = VT_DECIMAL;
+ return false;
+ }
+
+ case CV_OBJECT:
+ {
+ OBJECTREF obj = var->GetObjRef();
+ GCPROTECT_BEGIN(obj)
+ {
+ IUnknown *pUnk = NULL;
+
+ // Convert the object to an IDispatch/IUnknown pointer.
+ ComIpType FetchedIpType = ComIpType_None;
+ pUnk = GetComIPFromObjectRef(&obj, ComIpType_Both, &FetchedIpType);
+ V_UNKNOWN(oa) = pUnk;
+ V_VT(oa) = static_cast<VARTYPE>(FetchedIpType == ComIpType_Dispatch ? VT_DISPATCH : VT_UNKNOWN);
+ }
+ GCPROTECT_END();
+ return true;
+ }
+
+ default:
+ *dest = var->GetDataAsInt64();
+ V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType()));
+ return false;
+ }
+}
+
+// Converts an OleAut Variant into a COM+ Variant.
+// NOte that we pass the VariantData Byref so that if GC happens, 'var' gets updated
+void COMOAVariant::FromOAVariant(const VARIANT * const oa, VariantData * const& var)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(oa));
+ }
+ CONTRACTL_END;
+
+ // Clear the return variant value. It's allocated on
+ // the stack and we only want valid state data in there.
+ memset(var, 0, sizeof(VariantData));
+
+ CVTypes type = VTtoCV((VARENUM) V_VT(oa));
+ var->SetType(type);
+
+ switch (type)
+ {
+ case CV_STRING:
+ {
+ // BSTRs have an int with the string buffer length (not the string length)
+ // followed by the data. The pointer to the BSTR points to the start of the
+ // characters, NOT the start of the BSTR.
+ WCHAR * chars = V_BSTR(oa);
+ int strLen = SysStringLen(V_BSTR(oa));
+ STRINGREF str = StringObject::NewString(chars, strLen);
+ var->SetObjRef((OBJECTREF)str);
+ break;
+ }
+ case CV_DATETIME:
+ var->SetDataAsInt64(COMDateTime::DoubleDateToTicks(V_DATE(oa)));
+ break;
+
+ case CV_BOOLEAN:
+ var->SetDataAsInt64(V_BOOL(oa)==VARIANT_FALSE ? 0 : 1);
+ break;
+
+ case CV_DECIMAL:
+ {
+ MethodTable * pDecimalMT = GetTypeHandleForCVType(CV_DECIMAL).GetMethodTable();
+ _ASSERTE(pDecimalMT);
+ OBJECTREF pDecimalRef = AllocateObject(pDecimalMT);
+
+ *(DECIMAL *) pDecimalRef->GetData() = V_DECIMAL(oa);
+ var->SetObjRef(pDecimalRef);
+ break;
+ }
+
+
+ // All types less than 4 bytes need an explicit cast from their original
+ // type to be sign extended to 8 bytes. This makes Variant's ToInt32
+ // function simpler for these types.
+ case CV_I1:
+ var->SetDataAsInt64(V_I1(oa));
+ break;
+
+ case CV_U1:
+ var->SetDataAsInt64(V_UI1(oa));
+ break;
+
+ case CV_I2:
+ var->SetDataAsInt64(V_I2(oa));
+ break;
+
+ case CV_U2:
+ var->SetDataAsInt64(V_UI2(oa));
+ break;
+
+ case CV_EMPTY:
+ case CV_NULL:
+ // Must set up the Variant's m_or to the appropriate classes.
+ // Note that OleAut doesn't have any VT_MISSING.
+ VariantData::NewVariant(var, type, NULL
+ DEBUG_ARG(TRUE));
+ break;
+
+ case CV_OBJECT:
+ {
+ // Convert the IUnknown pointer to an OBJECTREF.
+ OBJECTREF oref = NULL;
+ GCPROTECT_BEGIN(oref);
+ GetObjectRefFromComIP(&oref, V_UNKNOWN(oa));
+ var->SetObjRef(oref);
+ GCPROTECT_END();
+ break;
+ }
+ default:
+ // Copy all the bits there, and make sure we don't do any float to int conversions.
+ void * src = (void*) &(V_UI1(oa));
+ var->SetData(src);
+ }
+}
+
+void COMOAVariant::OAFailed(const HRESULT hr)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(FAILED(hr));
+ }
+ CONTRACTL_END;
+
+ switch (hr)
+ {
+ case E_OUTOFMEMORY:
+ COMPlusThrowOM();
+
+ case DISP_E_BADVARTYPE:
+ COMPlusThrow(kNotSupportedException, W("NotSupported_OleAutBadVarType"));
+
+ case DISP_E_DIVBYZERO:
+ COMPlusThrow(kDivideByZeroException);
+
+ case DISP_E_OVERFLOW:
+ COMPlusThrow(kOverflowException);
+
+ case DISP_E_TYPEMISMATCH:
+ COMPlusThrow(kInvalidCastException, W("InvalidCast_OATypeMismatch"));
+
+ case E_INVALIDARG:
+ COMPlusThrow(kArgumentException);
+ break;
+
+ default:
+ _ASSERTE(!"Unrecognized HResult - OAVariantLib routine failed in an unexpected way!");
+ COMPlusThrowHR(hr);
+ }
+}
+
+FCIMPL6(void, COMOAVariant::ChangeTypeEx, VariantData *result, VariantData *op, LCID lcid, void *targetType, int cvType, INT16 flags)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(result));
+ }
+ CONTRACTL_END;
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+ GCPROTECT_BEGININTERIOR (result);
+
+ BOOL fConverted = FALSE;
+
+ TypeHandle thTarget = TypeHandle::FromPtr(targetType);
+ if (cvType == CV_OBJECT && IsTypeRefOrDef(g_ColorClassName, thTarget.GetModule(), thTarget.GetCl()))
+ {
+ if (op->GetType() == CV_I4 || op->GetType() == CV_U4)
+ {
+ // Int32/UInt32 can be converted to System.Drawing.Color
+ SYSTEMCOLOR SystemColor;
+ ConvertOleColorToSystemColor(op->GetDataAsUInt32(), &SystemColor);
+
+ result->SetObjRef(thTarget.AsMethodTable()->Box(&SystemColor));
+ result->SetType(CV_OBJECT);
+
+ fConverted = TRUE;
+ }
+ }
+
+ if (!fConverted)
+ {
+ VariantHolder ret;
+ VariantHolder vOp;
+
+ VARENUM vt = CVtoVT((CVTypes) cvType);
+ ToOAVariant(op, &vOp);
+
+ HRESULT hr = SafeVariantChangeTypeEx(&ret, &vOp, lcid, flags, static_cast<VARTYPE>(vt));
+
+ if (FAILED(hr))
+ OAFailed(hr);
+
+ if ((CVTypes) cvType == CV_CHAR)
+ {
+ result->SetType(CV_CHAR);
+ result->SetDataAsUInt16(V_UI2(&ret));
+ }
+ else
+ {
+ FromOAVariant(&ret, result);
+ }
+ }
+
+ GCPROTECT_END ();
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // FEATURE_COMINTEROP
diff --git a/src/classlibnative/bcltype/oavariant.h b/src/classlibnative/bcltype/oavariant.h
new file mode 100644
index 0000000000..30f7cb1958
--- /dev/null
+++ b/src/classlibnative/bcltype/oavariant.h
@@ -0,0 +1,46 @@
+// 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.
+//
+// File: OAVariant.h
+//
+
+//
+// Purpose: Wrapper for Ole Automation compatable math ops.
+// Calls through to OleAut.dll
+//
+
+//
+
+#ifndef _OAVARIANT_H_
+#define _OAVARIANT_H_
+
+#ifndef FEATURE_COMINTEROP
+#error FEATURE_COMINTEROP is required for this file
+#endif // FEATURE_COMINTEROP
+
+#include "variant.h"
+
+class COMOAVariant
+{
+public:
+
+ // Utility Functions
+ // Conversion between COM+ variant type field & OleAut Variant enumeration
+ // WinCE doesn't support Variants entirely.
+ static VARENUM CVtoVT(const CVTypes cv);
+ static CVTypes VTtoCV(const VARENUM vt);
+
+ // Conversion between COM+ Variant & OleAut Variant. ToOAVariant
+ // returns true if the conversion process allocated an object (like a BSTR).
+ static bool ToOAVariant(const VariantData * const var, VARIANT * oa);
+ static void FromOAVariant(const VARIANT * const oa, VariantData * const& var);
+
+ // Throw a specific exception for a failure, specified by a given HRESULT.
+ static void OAFailed(const HRESULT hr);
+
+ static FCDECL6(void, ChangeTypeEx, VariantData *result, VariantData *op, LCID lcid, void *targetType, int cvType, INT16 flags);
+};
+
+
+#endif // _OAVARIANT_H_
diff --git a/src/classlibnative/bcltype/objectnative.cpp b/src/classlibnative/bcltype/objectnative.cpp
new file mode 100644
index 0000000000..bf65ad1ff5
--- /dev/null
+++ b/src/classlibnative/bcltype/objectnative.cpp
@@ -0,0 +1,371 @@
+// 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.
+//
+// File: ObjectNative.cpp
+//
+
+//
+// Purpose: Native methods on System.Object
+//
+//
+
+#include "common.h"
+
+#include "objectnative.h"
+#include "excep.h"
+#include "vars.hpp"
+#include "field.h"
+#include "object.h"
+#include "comsynchronizable.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "eeconfig.h"
+#ifdef FEATURE_REMOTING
+#include "objectclone.h"
+#endif
+#include "mdaassistants.h"
+
+
+/********************************************************************/
+/* gets an object's 'value'. For normal classes, with reference
+ based semantics, this means the object's pointer. For boxed
+ primitive types, it also means just returning the pointer (because
+ they are immutable), for other value class, it means returning
+ a boxed copy. */
+
+FCIMPL1(Object*, ObjectNative::GetObjectValue, Object* obj)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ INJECT_FAULT(FCThrow(kOutOfMemoryException););
+ }
+ CONTRACTL_END;
+
+ VALIDATEOBJECT(obj);
+
+ if (obj == 0)
+ return(obj);
+
+ MethodTable* pMT = obj->GetMethodTable();
+ // optimize for primitive types since GetVerifierCorElementType is slow.
+ if (pMT->IsTruePrimitive() || TypeHandle(pMT).GetVerifierCorElementType() != ELEMENT_TYPE_VALUETYPE) {
+ return(obj);
+ }
+
+ Object* retVal = NULL;
+ OBJECTREF objRef(obj);
+ HELPER_METHOD_FRAME_BEGIN_RET_1(objRef); // Set up a frame
+
+ // Technically we could return boxed DateTimes and Decimals without
+ // copying them here, but VB realized that this would be a breaking change
+ // for their customers. So copy them.
+ //
+ // MethodTable::Box is a cleaner way to copy value class, but it is slower than following code.
+ //
+ retVal = OBJECTREFToObject(AllocateObject(pMT));
+ CopyValueClass(retVal->GetData(), objRef->GetData(), pMT, retVal->GetAppDomain());
+ HELPER_METHOD_FRAME_END();
+
+ return(retVal);
+}
+FCIMPLEND
+
+
+NOINLINE static INT32 GetHashCodeHelper(OBJECTREF objRef)
+{
+ DWORD idx = 0;
+
+ FC_INNER_PROLOG(ObjectNative::GetHashCode);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
+
+#ifdef MDA_SUPPORTED
+ MdaModuloObjectHashcode* pProbe = MDA_GET_ASSISTANT(ModuloObjectHashcode);
+#endif
+
+ idx = objRef->GetHashCodeEx();
+
+#ifdef MDA_SUPPORTED
+ if (pProbe)
+ idx = idx % pProbe->GetModulo();
+#endif
+
+ HELPER_METHOD_FRAME_END();
+ FC_INNER_EPILOG();
+ return idx;
+}
+
+// Note that we obtain a sync block index without actually building a sync block.
+// That's because a lot of objects are hashed, without requiring support for
+FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) {
+
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ INJECT_FAULT(FCThrow(kOutOfMemoryException););
+ }
+ CONTRACTL_END;
+
+ VALIDATEOBJECT(obj);
+
+ if (obj == 0)
+ return 0;
+
+ OBJECTREF objRef(obj);
+
+#ifdef MDA_SUPPORTED
+ if (!MDA_GET_ASSISTANT(ModuloObjectHashcode))
+#endif
+ {
+ DWORD bits = objRef->GetHeader()->GetBits();
+
+ if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX)
+ {
+ if (bits & BIT_SBLK_IS_HASHCODE)
+ {
+ // Common case: the object already has a hash code
+ return bits & MASK_HASHCODE;
+ }
+ else
+ {
+ // We have a sync block index. This means if we already have a hash code,
+ // it is in the sync block, otherwise we generate a new one and store it there
+ SyncBlock *psb = objRef->PassiveGetSyncBlock();
+ if (psb != NULL)
+ {
+ DWORD hashCode = psb->GetHashCode();
+ if (hashCode != 0)
+ return hashCode;
+ }
+ }
+ }
+ }
+
+ FC_INNER_RETURN(INT32, GetHashCodeHelper(objRef));
+}
+FCIMPLEND
+
+//
+// Compare by ref for normal classes, by value for value types.
+//
+// <TODO>@todo: it would be nice to customize this method based on the
+// defining class rather than doing a runtime check whether it is
+// a value type.</TODO>
+//
+
+FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ INJECT_FAULT(FCThrow(kOutOfMemoryException););
+ }
+ CONTRACTL_END;
+
+ if (pThisRef == pCompareRef)
+ FC_RETURN_BOOL(TRUE);
+
+ // Since we are in FCALL, we must handle NULL specially.
+ if (pThisRef == NULL || pCompareRef == NULL)
+ FC_RETURN_BOOL(FALSE);
+
+ MethodTable *pThisMT = pThisRef->GetMethodTable();
+
+ // If it's not a value class, don't compare by value
+ if (!pThisMT->IsValueType())
+ FC_RETURN_BOOL(FALSE);
+
+ // Make sure they are the same type.
+ if (pThisMT != pCompareRef->GetMethodTable())
+ FC_RETURN_BOOL(FALSE);
+
+ // Compare the contents (size - vtable - sync block index).
+ DWORD dwBaseSize = pThisRef->GetMethodTable()->GetBaseSize();
+ if(pThisRef->GetMethodTable() == g_pStringClass)
+ dwBaseSize -= sizeof(WCHAR);
+ BOOL ret = memcmp(
+ (void *) (pThisRef+1),
+ (void *) (pCompareRef+1),
+ dwBaseSize - sizeof(Object) - sizeof(int)) == 0;
+
+ FC_GC_POLL_RET();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+NOINLINE static Object* GetClassHelper(OBJECTREF objRef)
+{
+ FC_INNER_PROLOG(ObjectNative::GetClass);
+ _ASSERTE(objRef != NULL);
+ TypeHandle typeHandle = objRef->GetTypeHandle();
+ OBJECTREF refType = NULL;
+
+ // Arrays go down this slow path, at least don't do the full HelperMethodFrame setup
+ // if we are fetching the cached entry.
+ refType = typeHandle.GetManagedClassObjectFast();
+ if (refType != NULL)
+ return OBJECTREFToObject(refType);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refType);
+
+#ifdef FEATURE_REMOTING
+ if (objRef->IsTransparentProxy())
+ refType = CRemotingServices::GetClass(objRef);
+ else
+#endif
+ refType = typeHandle.GetManagedClassObject();
+
+ HELPER_METHOD_FRAME_END();
+ FC_INNER_EPILOG();
+ return OBJECTREFToObject(refType);
+}
+
+// This routine is called by the Object.GetType() routine. It is a major way to get the Sytem.Type
+FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ INJECT_FAULT(FCThrow(kOutOfMemoryException););
+ }
+ CONTRACTL_END;
+
+ OBJECTREF objRef = ObjectToOBJECTREF(pThis);
+ if (objRef != NULL)
+ {
+ MethodTable* pMT = objRef->GetMethodTable();
+ OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists();
+ if (typePtr != NULL)
+ {
+ return OBJECTREFToObject(typePtr);
+ }
+ }
+ else
+ FCThrow(kNullReferenceException);
+
+ FC_INNER_RETURN(Object*, GetClassHelper(objRef));
+}
+FCIMPLEND
+
+FCIMPL1(Object*, ObjectNative::Clone, Object* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF refClone = NULL;
+ OBJECTREF refThis = ObjectToOBJECTREF(pThisUNSAFE);
+
+ if (refThis == NULL)
+ FCThrow(kNullReferenceException);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_2(refClone, refThis);
+
+ // ObjectNative::Clone() ensures that the source and destination are always in
+ // the same context.
+
+ MethodTable* pMT = refThis->GetMethodTable();
+
+ // assert that String has overloaded the Clone() method
+ _ASSERTE(pMT != g_pStringClass);
+
+ if (pMT->IsArray()) {
+ refClone = DupArrayForCloning((BASEARRAYREF)refThis);
+ } else {
+ // We don't need to call the <cinit> because we know
+ // that it has been called....(It was called before this was created)
+ refClone = AllocateObject(pMT);
+ }
+
+ SIZE_T cb = refThis->GetSize() - sizeof(ObjHeader);
+
+ // copy contents of "this" to the clone
+ if (pMT->ContainsPointers())
+ {
+ memmoveGCRefs(OBJECTREFToObject(refClone), OBJECTREFToObject(refThis), cb);
+ }
+ else
+ {
+ memcpyNoGCRefs(OBJECTREFToObject(refClone), OBJECTREFToObject(refThis), cb);
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(refClone);
+}
+FCIMPLEND
+
+FCIMPL3(FC_BOOL_RET, ObjectNative::WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ BOOL retVal = FALSE;
+ OBJECTREF pThis = (OBJECTREF) pThisUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
+
+ if (pThis == NULL)
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+
+ if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT))
+ COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegNum"));
+
+ retVal = pThis->Wait(Timeout, exitContext);
+
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(retVal);
+}
+FCIMPLEND
+
+FCIMPL1(void, ObjectNative::Pulse, Object* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF pThis = (OBJECTREF) pThisUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_1(pThis);
+
+ if (pThis == NULL)
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+
+ pThis->Pulse();
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL1(void, ObjectNative::PulseAll, Object* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF pThis = (OBJECTREF) pThisUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_1(pThis);
+
+ if (pThis == NULL)
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+
+ pThis->PulseAll();
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL1(FC_BOOL_RET, ObjectNative::IsLockHeld, Object* pThisUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ BOOL retVal;
+ DWORD owningThreadId;
+ DWORD acquisitionCount;
+
+ //
+ // If the lock is held, check if it's held by the current thread.
+ //
+ retVal = pThisUNSAFE->GetThreadOwningMonitorLock(&owningThreadId, &acquisitionCount);
+ if (retVal)
+ retVal = GetThread()->GetThreadId() == owningThreadId;
+
+ FC_RETURN_BOOL(retVal);
+}
+FCIMPLEND
+
diff --git a/src/classlibnative/bcltype/objectnative.h b/src/classlibnative/bcltype/objectnative.h
new file mode 100644
index 0000000000..573b04ea0a
--- /dev/null
+++ b/src/classlibnative/bcltype/objectnative.h
@@ -0,0 +1,43 @@
+// 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.
+//
+// File: ObjectNative.h
+//
+
+//
+// Purpose: Native methods on System.Object
+//
+//
+
+#ifndef _OBJECTNATIVE_H_
+#define _OBJECTNATIVE_H_
+
+#include "fcall.h"
+
+
+//
+// Each function that we call through native only gets one argument,
+// which is actually a pointer to it's stack of arguments. Our structs
+// for accessing these are defined below.
+//
+
+class ObjectNative
+{
+public:
+
+ // This method will return a Class object for the object
+ // iff the Class object has already been created.
+ // If the Class object doesn't exist then you must call the GetClass() method.
+ static FCDECL1(Object*, GetObjectValue, Object* vThisRef);
+ static FCDECL1(INT32, GetHashCode, Object* vThisRef);
+ static FCDECL2(FC_BOOL_RET, Equals, Object *pThisRef, Object *pCompareRef);
+ static FCDECL1(Object*, Clone, Object* pThis);
+ static FCDECL1(Object*, GetClass, Object* pThis);
+ static FCDECL3(FC_BOOL_RET, WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE);
+ static FCDECL1(void, Pulse, Object* pThisUNSAFE);
+ static FCDECL1(void, PulseAll, Object* pThisUNSAFE);
+ static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE);
+};
+
+#endif // _OBJECTNATIVE_H_
diff --git a/src/classlibnative/bcltype/stringbuffer.cpp b/src/classlibnative/bcltype/stringbuffer.cpp
new file mode 100644
index 0000000000..d060a7e1ba
--- /dev/null
+++ b/src/classlibnative/bcltype/stringbuffer.cpp
@@ -0,0 +1,48 @@
+// 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.
+//
+// File: StringBuffer.cpp
+//
+
+//
+// Purpose: The implementation of the StringBuffer class.
+//
+//
+
+#include "common.h"
+
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "string.h"
+#include "stringbuffer.h"
+
+FCIMPL3(void, COMStringBuffer::ReplaceBufferInternal, StringBufferObject* thisRefUNSAFE, __in_ecount(newLength) WCHAR* newBuffer, INT32 newLength)
+{
+ FCALL_CONTRACT;
+
+ STRINGBUFFERREF thisRef = (STRINGBUFFERREF)ObjectToOBJECTREF(thisRefUNSAFE);
+ HELPER_METHOD_FRAME_BEGIN_1(thisRef);
+
+ StringBufferObject::ReplaceBuffer(&thisRef, newBuffer, newLength);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL3(void, COMStringBuffer::ReplaceBufferAnsiInternal, StringBufferObject* thisRefUNSAFE, __in_ecount(newCapacity) CHAR* newBuffer, INT32 newCapacity)
+{
+ FCALL_CONTRACT;
+
+ STRINGBUFFERREF thisRef = (STRINGBUFFERREF)ObjectToOBJECTREF(thisRefUNSAFE);
+ HELPER_METHOD_FRAME_BEGIN_1(thisRef);
+
+ StringBufferObject::ReplaceBufferAnsi(&thisRef, newBuffer, newCapacity);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
diff --git a/src/classlibnative/bcltype/stringbuffer.h b/src/classlibnative/bcltype/stringbuffer.h
new file mode 100644
index 0000000000..4c151ef166
--- /dev/null
+++ b/src/classlibnative/bcltype/stringbuffer.h
@@ -0,0 +1,38 @@
+// 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.
+//
+// File: StringBuffer.h
+//
+
+//
+// Purpose: Contains types and method signatures for the
+// StringBuffer class.
+//
+// Each function that we call through native only gets one argument,
+// which is actually a pointer to it's stack of arguments. Our structs
+// for accessing these are defined below.
+//
+
+//
+
+#ifndef _STRINGBUFFER_H_
+#define _STRINGBUFFER_H_
+
+#define CAPACITY_LOW 10000
+#define CAPACITY_MID 15000
+#define CAPACITY_HIGH 20000
+#define CAPACITY_FIXEDINC 5000
+#define CAPACITY_PERCENTINC 1.25
+
+class COMStringBuffer {
+
+public:
+ //
+ // NATIVE HELPER METHODS
+ //
+ static FCDECL3(void, ReplaceBufferInternal, StringBufferObject* thisRefUNSAFE, __in_ecount(newLength) WCHAR* newBuffer, INT32 newLength);
+ static FCDECL3(void, ReplaceBufferAnsiInternal, StringBufferObject* thisRefUNSAFE, __in_ecount(newCapacity) CHAR* newBuffer, INT32 newCapacity);
+};
+
+#endif // _STRINGBUFFER_H
diff --git a/src/classlibnative/bcltype/stringnative.cpp b/src/classlibnative/bcltype/stringnative.cpp
new file mode 100644
index 0000000000..1e6b132b17
--- /dev/null
+++ b/src/classlibnative/bcltype/stringnative.cpp
@@ -0,0 +1,736 @@
+// 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.
+//
+// File: StringNative.cpp
+//
+
+//
+// Purpose: The implementation of the String class.
+//
+
+//
+
+#include "common.h"
+
+#include "object.h"
+#include "utilcode.h"
+#include "excep.h"
+#include "frames.h"
+#include "field.h"
+#include "vars.hpp"
+#include "stringnative.h"
+#include "stringbuffer.h"
+#include "comutilnative.h"
+#include "metasig.h"
+#include "excep.h"
+
+// Compile the string functionality with these pragma flags (equivalent of the command line /Ox flag)
+// Compiling this functionality differently gives us significant throughout gain in some cases.
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("tgy", on)
+#endif
+
+
+#define PROBABILISTICMAP_BLOCK_INDEX_MASK 0X7
+#define PROBABILISTICMAP_BLOCK_INDEX_SHIFT 0x3
+#define PROBABILISTICMAP_SIZE 0X8
+
+//
+//
+// FORWARD DECLARATIONS
+//
+//
+int ArrayContains(WCHAR searchChar, __in_ecount(length) const WCHAR *begin, int length);
+void InitializeProbabilisticMap(int* charMap, __in_ecount(length) const WCHAR* charArray, int length);
+bool ProbablyContains(const int* charMap, WCHAR searchChar);
+bool IsBitSet(int value, int bitPos);
+void SetBit(int* value, int bitPos);
+
+//
+//
+// CONSTRUCTORS
+//
+//
+
+/*===========================StringInitSBytPtrPartialEx===========================
+**Action: Takes a byte *, startIndex, length, and encoding and turns this into a string.
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+
+FCIMPL5(Object *, COMString::StringInitSBytPtrPartialEx, StringObject *thisString,
+ I1 *ptr, INT32 startIndex, INT32 length, Object *encoding)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(thisString == 0);
+ PRECONDITION(ptr != NULL);
+ } CONTRACTL_END;
+
+ STRINGREF pString = NULL;
+ VALIDATEOBJECT(encoding);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(encoding);
+ MethodDescCallSite createString(METHOD__STRING__CREATE_STRING);
+
+ ARG_SLOT args[] = {
+ PtrToArgSlot(ptr),
+ startIndex,
+ length,
+ ObjToArgSlot(ObjectToOBJECTREF(encoding)),
+ };
+
+ pString = createString.Call_RetSTRINGREF(args);
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(pString);
+}
+FCIMPLEND
+
+/*==============================StringInitCharPtr===============================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+FCIMPL2(Object *, COMString::StringInitCharPtr, StringObject *stringThis, INT8 *ptr)
+{
+ FCALL_CONTRACT;
+
+ _ASSERTE(stringThis == 0); // This is the constructor
+ Object *result = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+ result = OBJECTREFToObject(StringObject::StringInitCharHelper((LPCSTR)ptr, -1));
+ HELPER_METHOD_FRAME_END();
+ return result;
+}
+FCIMPLEND
+
+/*===========================StringInitCharPtrPartial===========================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+FCIMPL4(Object *, COMString::StringInitCharPtrPartial, StringObject *stringThis, INT8 *value,
+ INT32 startIndex, INT32 length)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(stringThis ==0);
+ } CONTRACTL_END;
+
+ STRINGREF pString = NULL;
+
+ //Verify the args.
+ if (startIndex<0) {
+ FCThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_StartIndex"));
+ }
+
+ if (length<0) {
+ FCThrowArgumentOutOfRange(W("length"), W("ArgumentOutOfRange_NegativeLength"));
+ }
+
+ // This is called directly now. There is no check in managed code.
+ if( value == NULL) {
+ FCThrowArgumentNull(W("value"));
+ }
+
+ LPCSTR pBase = (LPCSTR)value;
+ LPCSTR pFrom = pBase + startIndex;
+ if (pFrom < pBase) {
+ // Check for overflow of pointer addition
+ FCThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_PartialWCHAR"));
+ }
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ pString = StringObject::StringInitCharHelper(pFrom, length);
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(pString);
+}
+FCIMPLEND
+
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+
+inline COMNlsHashProvider * GetCurrentNlsHashProvider()
+{
+ LIMITED_METHOD_CONTRACT;
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+ return curDomain->m_pNlsHashProvider;
+#else
+ return &COMNlsHashProvider::s_NlsHashProvider;
+#endif // FEATURE_CORECLR
+}
+
+FCIMPL3(INT32, COMString::Marvin32HashString, StringObject* thisRefUNSAFE, INT32 strLen, INT64 additionalEntropy) {
+ FCALL_CONTRACT;
+
+ int iReturnHash = 0;
+
+ if (thisRefUNSAFE == NULL) {
+ FCThrow(kNullReferenceException);
+ }
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
+ iReturnHash = GetCurrentNlsHashProvider()->HashString(thisRefUNSAFE->GetBuffer(), thisRefUNSAFE->GetStringLength(), TRUE, additionalEntropy);
+ END_SO_INTOLERANT_CODE;
+
+ FC_GC_POLL_RET();
+
+ return iReturnHash;
+}
+FCIMPLEND
+
+BOOL QCALLTYPE COMString::UseRandomizedHashing() {
+ QCALL_CONTRACT;
+
+ BOOL bUseRandomizedHashing = FALSE;
+
+ BEGIN_QCALL;
+
+ bUseRandomizedHashing = GetCurrentNlsHashProvider()->GetUseRandomHashing();
+
+ END_QCALL;
+
+ return bUseRandomizedHashing;
+}
+#endif
+
+/*===============================IsFastSort===============================
+**Action: Call the helper to walk the string and see if we have any high chars.
+**Returns: void. The appropriate bits are set on the String.
+**Arguments: vThisRef - The string to be checked.
+**Exceptions: None.
+==============================================================================*/
+FCIMPL1(FC_BOOL_RET, COMString::IsFastSort, StringObject* thisRef) {
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(thisRef);
+ _ASSERTE(thisRef!=NULL);
+ DWORD state = thisRef->GetHighCharState();
+ if (IS_STRING_STATE_UNDETERMINED(state)) {
+ state = (STRINGREF(thisRef))->InternalCheckHighChars();
+ FC_GC_POLL_RET();
+ }
+ else {
+ FC_GC_POLL_NOT_NEEDED();
+ }
+ FC_RETURN_BOOL(IS_FAST_SORT(state)); //This can indicate either high chars or special sorting chars.
+}
+FCIMPLEND
+
+/*===============================IsAscii===============================
+**Action: Call the helper to walk the string and see if we have any high chars.
+**Returns: void. The appropriate bits are set on the String.
+**Arguments: vThisRef - The string to be checked.
+**Exceptions: None.
+==============================================================================*/
+FCIMPL1(FC_BOOL_RET, COMString::IsAscii, StringObject* thisRef) {
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(thisRef);
+ _ASSERTE(thisRef!=NULL);
+ DWORD state = thisRef->GetHighCharState();
+ if (IS_STRING_STATE_UNDETERMINED(state)) {
+ state = (STRINGREF(thisRef))->InternalCheckHighChars();
+ FC_GC_POLL_RET();
+ }
+ else {
+ FC_GC_POLL_NOT_NEEDED();
+ }
+ FC_RETURN_BOOL(IS_ASCII(state)); //This can indicate either high chars or special sorting chars.
+}
+FCIMPLEND
+
+
+
+//This function relies on the fact that we put a terminating null on the end of
+//all managed strings.
+FCIMPL2(INT32, COMString::FCCompareOrdinalIgnoreCaseWC, StringObject* strA, __in_z INT8 *strBChars) {
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(strA);
+ WCHAR *strAChars;
+ WCHAR *strAStart;
+ INT32 aLength;
+ INT32 ret;
+
+ _ASSERT(strA != NULL && strBChars != NULL);
+
+ //Get our data.
+ strA->RefInterpretGetStringValuesDangerousForGC((WCHAR **) &strAChars, &aLength);
+
+ //Record the start pointer for some comparisons at the end.
+ strAStart = strAChars;
+
+ if (!StringObject::CaseInsensitiveCompHelper(strAChars, strBChars, aLength, -1, &ret)) {
+ //This will happen if we have characters greater than 0x7F. This indicates that the function failed.
+ // We don't throw an exception here. You can look at the success value returned to do something meaningful.
+ ret = 1;
+ }
+
+ FC_GC_POLL_RET();
+ return ret;
+}
+FCIMPLEND
+
+/*================================CompareOrdinalEx===============================
+**Args: typedef struct {STRINGREF thisRef; INT32 options; INT32 length; INT32 valueOffset;\
+ STRINGREF value; INT32 thisOffset;} _compareOrdinalArgsEx;
+==============================================================================*/
+
+FCIMPL6(INT32, COMString::CompareOrdinalEx, StringObject* strA, INT32 indexA, INT32 countA, StringObject* strB, INT32 indexB, INT32 countB)
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(strA);
+ VALIDATEOBJECT(strB);
+ DWORD *strAChars, *strBChars;
+ int strALength, strBLength;
+
+ // These runtime tests are handled in the managed wrapper.
+ _ASSERTE(strA != NULL && strB != NULL);
+ _ASSERTE(indexA >= 0 && indexB >= 0);
+ _ASSERTE(countA >= 0 && countB >= 0);
+
+ strA->RefInterpretGetStringValuesDangerousForGC((WCHAR **) &strAChars, &strALength);
+ strB->RefInterpretGetStringValuesDangerousForGC((WCHAR **) &strBChars, &strBLength);
+
+ _ASSERTE(countA <= strALength - indexA);
+ _ASSERTE(countB <= strBLength - indexB);
+
+ // Set up the loop variables.
+ strAChars = (DWORD *) ((WCHAR *) strAChars + indexA);
+ strBChars = (DWORD *) ((WCHAR *) strBChars + indexB);
+
+ INT32 result = StringObject::FastCompareStringHelper(strAChars, countA, strBChars, countB);
+
+ FC_GC_POLL_RET();
+ return result;
+
+}
+FCIMPLEND
+
+/*===============================IndexOfCharArray===============================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+FCIMPL4(INT32, COMString::IndexOfCharArray, StringObject* thisRef, CHARArray* valueRef, INT32 startIndex, INT32 count )
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(thisRef);
+ VALIDATEOBJECT(valueRef);
+
+ if (thisRef == NULL)
+ FCThrow(kNullReferenceException);
+ if (valueRef == NULL)
+ FCThrowArgumentNull(W("anyOf"));
+
+ WCHAR *thisChars;
+ WCHAR *valueChars;
+ int valueLength;
+ int thisLength;
+
+ thisRef->RefInterpretGetStringValuesDangerousForGC(&thisChars, &thisLength);
+
+ if (startIndex < 0 || startIndex > thisLength) {
+ FCThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_Index"));
+ }
+
+ if (count < 0 || count > thisLength - startIndex) {
+ FCThrowArgumentOutOfRange(W("count"), W("ArgumentOutOfRange_Count"));
+ }
+
+ int endIndex = startIndex + count;
+
+ valueLength = valueRef->GetNumComponents();
+ valueChars = (WCHAR *)valueRef->GetDataPtr();
+
+ // use probabilistic map, see (code:InitializeProbabilisticMap)
+ int charMap[PROBABILISTICMAP_SIZE] = {0};
+
+ InitializeProbabilisticMap(charMap, valueChars, valueLength);
+
+ for(int i = startIndex; i < endIndex; i++) {
+ WCHAR thisChar = thisChars[i];
+ if (ProbablyContains(charMap, thisChar))
+ if (ArrayContains(thisChars[i], valueChars, valueLength) >= 0) {
+ FC_GC_POLL_RET();
+ return i;
+ }
+ }
+
+ FC_GC_POLL_RET();
+ return -1;
+}
+FCIMPLEND
+
+/*=============================LastIndexOfCharArray=============================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+
+FCIMPL4(INT32, COMString::LastIndexOfCharArray, StringObject* thisRef, CHARArray* valueRef, INT32 startIndex, INT32 count )
+{
+ FCALL_CONTRACT;
+
+ VALIDATEOBJECT(thisRef);
+ VALIDATEOBJECT(valueRef);
+ WCHAR *thisChars, *valueChars;
+ int thisLength, valueLength;
+
+ if (thisRef==NULL) {
+ FCThrow(kNullReferenceException);
+ }
+
+ if (valueRef == NULL)
+ FCThrowArgumentNull(W("anyOf"));
+
+ thisRef->RefInterpretGetStringValuesDangerousForGC(&thisChars, &thisLength);
+
+ if (thisLength == 0) {
+ return -1;
+ }
+
+ if (startIndex < 0 || startIndex >= thisLength) {
+ FCThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_Index"));
+ }
+
+ if (count<0 || count - 1 > startIndex) {
+ FCThrowArgumentOutOfRange(W("count"), W("ArgumentOutOfRange_Count"));
+ }
+
+
+ valueLength = valueRef->GetNumComponents();
+ valueChars = (WCHAR *)valueRef->GetDataPtr();
+
+ int endIndex = startIndex - count + 1;
+
+ // use probabilistic map, see (code:InitializeProbabilisticMap)
+ int charMap[PROBABILISTICMAP_SIZE] = {0};
+
+ InitializeProbabilisticMap(charMap, valueChars, valueLength);
+
+ //We search [startIndex..EndIndex]
+ for (int i=startIndex; i>=endIndex; i--) {
+ WCHAR thisChar = thisChars[i];
+ if (ProbablyContains(charMap, thisChar))
+ if (ArrayContains(thisChars[i],valueChars, valueLength) >= 0) {
+ FC_GC_POLL_RET();
+ return i;
+ }
+ }
+
+ FC_GC_POLL_RET();
+ return -1;
+
+}
+FCIMPLEND
+
+/*==================================GETCHARAT===================================
+**Returns the character at position index. Thows IndexOutOfRangeException as
+**appropriate.
+**This method is not actually used. JIT will generate code for indexer method on string class.
+**
+==============================================================================*/
+FCIMPL2(FC_CHAR_RET, COMString::GetCharAt, StringObject* str, INT32 index) {
+ FCALL_CONTRACT;
+
+ FC_GC_POLL_NOT_NEEDED();
+ VALIDATEOBJECT(str);
+ if (str == NULL) {
+ FCThrow(kNullReferenceException);
+ }
+ _ASSERTE(str->GetMethodTable() == g_pStringClass);
+
+ if (index >=0 && index < (INT32)str->GetStringLength()) {
+ //Return the appropriate character.
+ return str->GetBuffer()[index];
+ }
+
+ FCThrow(kIndexOutOfRangeException);
+}
+FCIMPLEND
+
+
+/*==================================LENGTH=================================== */
+
+FCIMPL1(INT32, COMString::Length, StringObject* str) {
+ FCALL_CONTRACT;
+
+ FC_GC_POLL_NOT_NEEDED();
+ if (str == NULL)
+ FCThrow(kNullReferenceException);
+
+ FCUnique(0x11);
+ return str->GetStringLength();
+}
+FCIMPLEND
+
+
+// HELPER METHODS
+//
+//
+// A probabilistic map is an optimization that is used in IndexOfAny/
+// LastIndexOfAny methods. The idea is to create a bit map of the characters we
+// are searching for and use this map as a "cheap" check to decide if the
+// current character in the string exists in the array of input characters.
+// There are 256 bits in the map, with each character mapped to 2 bits. Every
+// character is divided into 2 bytes, and then every byte is mapped to 1 bit.
+// The character map is an array of 8 integers acting as map blocks. The 3 lsb
+// in each byte in the character is used to index into this map to get the
+// right block, the value of the remaining 5 msb are used as the bit position
+// inside this block.
+void InitializeProbabilisticMap(int* charMap, __in_ecount(length) const WCHAR* charArray, int length) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(charMap != NULL);
+ _ASSERTE(charArray != NULL);
+ _ASSERTE(length >= 0);
+
+ for(int i = 0; i < length; ++i) {
+ int hi,lo;
+
+ WCHAR c = charArray[i];
+
+ hi = (c >> 8) & 0xFF;
+ lo = c & 0xFF;
+
+ int* value = &charMap[lo & PROBABILISTICMAP_BLOCK_INDEX_MASK];
+ SetBit(value, lo >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
+
+ value = &charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK];
+ SetBit(value, hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
+ }
+}
+
+// Use the probabilistic map to decide if the character value exists in the
+// map. When this method return false, we are certain the character doesn't
+// exist, however a true return means it *may* exist.
+inline bool ProbablyContains(const int* charMap, WCHAR searchValue) {
+ LIMITED_METHOD_CONTRACT;
+
+ int lo, hi;
+
+ lo = searchValue & 0xFF;
+ int value = charMap[lo & PROBABILISTICMAP_BLOCK_INDEX_MASK];
+
+ if (IsBitSet(value, lo >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT)) {
+ hi = (searchValue >> 8) & 0xFF;
+ value = charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK];
+
+ return IsBitSet(value, hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
+ }
+
+ return false;
+}
+
+inline void SetBit(int* value, int bitPos) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(bitPos <= 31);
+
+ *value |= (1 << bitPos);
+}
+
+inline bool IsBitSet(int value, int bitPos) {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(bitPos <= 31);
+
+ return (value & (1 << bitPos)) != 0;
+}
+
+
+/*================================ArrayContains=================================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+int ArrayContains(WCHAR searchChar, __in_ecount(length) const WCHAR *begin, int length) {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(begin != NULL);
+ _ASSERTE(length >= 0);
+
+ for(int i = 0; i < length; i++) {
+ if(begin[i] == searchChar) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/*================================ReplaceString=================================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+FCIMPL3(Object*, COMString::ReplaceString, StringObject* thisRefUNSAFE, StringObject* oldValueUNSAFE, StringObject* newValueUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ struct _gc
+ {
+ STRINGREF thisRef;
+ STRINGREF oldValue;
+ STRINGREF newValue;
+ STRINGREF retValString;
+ } gc;
+
+ gc.thisRef = ObjectToSTRINGREF(thisRefUNSAFE);
+ gc.oldValue = ObjectToSTRINGREF(oldValueUNSAFE);
+ gc.newValue = ObjectToSTRINGREF(newValueUNSAFE);
+ gc.retValString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ int *replaceIndex;
+ int index=0;
+ int replaceCount=0;
+ int readPos, writePos;
+ WCHAR *thisBuffer, *oldBuffer, *newBuffer, *retValBuffer;
+ int thisLength, oldLength, newLength;
+ int endIndex;
+ CQuickBytes replaceIndices;
+
+
+ if (gc.thisRef==NULL) {
+ COMPlusThrow(kNullReferenceException, W("NullReference_This"));
+ }
+
+ //Verify all of the arguments.
+ if (!gc.oldValue) {
+ COMPlusThrowArgumentNull(W("oldValue"), W("ArgumentNull_Generic"));
+ }
+
+ //If they asked to replace oldValue with a null, replace all occurances
+ //with the empty string.
+ if (!gc.newValue) {
+ gc.newValue = StringObject::GetEmptyString();
+ }
+
+ gc.thisRef->RefInterpretGetStringValuesDangerousForGC(&thisBuffer, &thisLength);
+ gc.oldValue->RefInterpretGetStringValuesDangerousForGC(&oldBuffer, &oldLength);
+ gc.newValue->RefInterpretGetStringValuesDangerousForGC(&newBuffer, &newLength);
+
+ //Record the endIndex so that we don't need to do this calculation all over the place.
+ endIndex = thisLength;
+
+ //If our old Length is 0, we won't know what to replace
+ if (oldLength==0) {
+ COMPlusThrowArgumentException(W("oldValue"), W("Argument_StringZeroLength"));
+ }
+
+ //replaceIndex is made large enough to hold the maximum number of replacements possible:
+ //The case where every character in the current buffer gets replaced.
+ replaceIndex = (int *)replaceIndices.AllocThrows((thisLength/oldLength+1)*sizeof(int));
+ index=0;
+
+ _ASSERTE(endIndex - oldLength <= endIndex);
+ //Prefix: oldLength validated in mscorlib.dll!String.Replace
+ PREFIX_ASSUME(endIndex - oldLength <= endIndex);
+
+ while (((index=StringBufferObject::LocalIndexOfString(thisBuffer,oldBuffer,thisLength,oldLength,index))>-1) && (index<=endIndex-oldLength))
+ {
+ replaceIndex[replaceCount++] = index;
+ index+=oldLength;
+ }
+
+ if (replaceCount != 0)
+ {
+ //Calculate the new length of the string and ensure that we have sufficent room.
+ INT64 retValBuffLength = thisLength - ((oldLength - newLength) * (INT64)replaceCount);
+ _ASSERTE(retValBuffLength >= 0);
+ if (retValBuffLength > 0x7FFFFFFF)
+ COMPlusThrowOM();
+
+ gc.retValString = StringObject::NewString((INT32)retValBuffLength);
+ retValBuffer = gc.retValString->GetBuffer();
+
+ //Get the update buffers for all the Strings since the allocation could have triggered a GC.
+ thisBuffer = gc.thisRef->GetBuffer();
+ newBuffer = gc.newValue->GetBuffer();
+ oldBuffer = gc.oldValue->GetBuffer();
+
+
+ //Set replaceHolder to be the upper limit of our array.
+ int replaceHolder = replaceCount;
+ replaceCount=0;
+
+ //Walk the array forwards copying each character as we go. If we reach an instance
+ //of the string being replaced, replace the old string with the new string.
+ readPos = 0;
+ writePos = 0;
+ while (readPos<thisLength)
+ {
+ if (replaceCount<replaceHolder&&readPos==replaceIndex[replaceCount])
+ {
+ replaceCount++;
+ readPos+=(oldLength);
+ memcpyNoGCRefs(&retValBuffer[writePos], newBuffer, newLength*sizeof(WCHAR));
+ writePos+=(newLength);
+ }
+ else
+ {
+ retValBuffer[writePos++] = thisBuffer[readPos++];
+ }
+ }
+ retValBuffer[retValBuffLength]='\0';
+ }
+ else
+ {
+ gc.retValString = gc.thisRef;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.retValString);
+}
+FCIMPLEND
+
+
+
+#ifdef FEATURE_COMINTEROP
+
+FCIMPL2(FC_BOOL_RET, COMString::FCTryGetTrailByte, StringObject* thisRefUNSAFE, UINT8 *pbData)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF thisRef = ObjectToSTRINGREF(thisRefUNSAFE);
+ FC_RETURN_BOOL(thisRef->GetTrailByte(pbData));
+}
+FCIMPLEND
+
+FCIMPL2(VOID, COMString::FCSetTrailByte, StringObject* thisRefUNSAFE, UINT8 bData)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF thisRef = ObjectToSTRINGREF(thisRefUNSAFE);
+ HELPER_METHOD_FRAME_BEGIN_1(thisRef);
+
+ thisRef->SetTrailByte(bData);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+#endif // FEATURE_COMINTEROP
+
+// Revert to command line compilation flags
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize ("", on)
+#endif
diff --git a/src/classlibnative/bcltype/stringnative.h b/src/classlibnative/bcltype/stringnative.h
new file mode 100644
index 0000000000..a4d962df6d
--- /dev/null
+++ b/src/classlibnative/bcltype/stringnative.h
@@ -0,0 +1,106 @@
+// 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.
+//
+// File: StringNative.h
+//
+
+//
+// Purpose: Contains types and method signatures for the String class
+//
+
+//
+
+#include "fcall.h"
+#include "qcall.h"
+#include "excep.h"
+
+#ifndef _STRINGNATIVE_H_
+#define _STRINGNATIVE_H_
+//
+// Each function that we call through native only gets one argument,
+// which is actually a pointer to it's stack of arguments. Our structs
+// for accessing these are defined below.
+//
+
+//
+//These are the type signatures for String
+//
+//
+// The method signatures for each of the methods we define.
+// N.B.: There's a one-to-one mapping between the method signatures and the
+// type definitions given above.
+//
+
+
+// Compile the string functionality with these pragma flags (equivalent of the command line /Ox flag)
+// Compiling this functionality differently gives us significant throughout gain in some cases.
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("tgy", on)
+#endif
+
+class COMString {
+public:
+
+ //
+ // Constructors
+ //
+ static FCDECL5(Object *, StringInitSBytPtrPartialEx, StringObject *thisString,
+ I1 *ptr, INT32 startIndex, INT32 length, Object* encoding);
+ static FCDECL2(Object *, StringInitCharPtr, StringObject *stringThis, INT8 *ptr);
+ static FCDECL4(Object *, StringInitCharPtrPartial, StringObject *stringThis, INT8 *value,
+ INT32 startIndex, INT32 length);
+
+ //
+ // Search/Query Methods
+ //
+ static FCDECL1(FC_BOOL_RET, IsFastSort, StringObject* pThisRef);
+ static FCDECL1(FC_BOOL_RET, IsAscii, StringObject* pThisRef);
+
+ static FCDECL2(INT32, FCCompareOrdinalIgnoreCaseWC, StringObject* strA, __in_z INT8 *strB);
+
+ static FCDECL6(INT32, CompareOrdinalEx, StringObject* strA, INT32 indexA, INT32 countA, StringObject* strB, INT32 indexB, INT32 countB);
+
+ static FCDECL4(INT32, LastIndexOfCharArray, StringObject* thisRef, CHARArray* valueRef, INT32 startIndex, INT32 count );
+
+ static FCDECL4(INT32, IndexOfCharArray, StringObject* vThisRef, CHARArray* value, INT32 startIndex, INT32 count );
+
+ static FCDECL2(FC_CHAR_RET, GetCharAt, StringObject* pThisRef, INT32 index);
+ static FCDECL1(INT32, Length, StringObject* pThisRef);
+
+ //
+ // Modifiers
+ //
+ static FCDECL3(Object*, ReplaceString, StringObject* thisRef, StringObject* oldValue, StringObject* newValue);
+
+ static FCDECL3(Object*, Insert, StringObject* thisRefUNSAFE, INT32 startIndex, StringObject* valueUNSAFE);
+
+ static FCDECL3(Object*, Remove, StringObject* thisRefUNSAFE, INT32 startIndex, INT32 count);
+
+ //
+ // Interop
+ //
+#ifdef FEATURE_COMINTEROP
+ static FCDECL2(FC_BOOL_RET, FCTryGetTrailByte, StringObject* thisRefUNSAFE, UINT8 *pbData);
+ static FCDECL2(VOID, FCSetTrailByte, StringObject* thisRefUNSAFE, UINT8 bData);
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+ static FCDECL3(INT32, Marvin32HashString, StringObject* thisRefUNSAFE, INT32 strLen, INT64 additionalEntropy);
+ static BOOL QCALLTYPE UseRandomizedHashing();
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+
+};
+
+// Revert to command line compilation flags
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize ("", on)
+#endif
+
+#endif // _STRINGNATIVE_H_
+
+
+
+
+
+
diff --git a/src/classlibnative/bcltype/system.cpp b/src/classlibnative/bcltype/system.cpp
new file mode 100644
index 0000000000..e902734b23
--- /dev/null
+++ b/src/classlibnative/bcltype/system.cpp
@@ -0,0 +1,888 @@
+// 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.
+//
+// File: System.cpp
+//
+
+//
+// Purpose: Native methods on System.Environment & Array
+//
+
+//
+
+#include "common.h"
+
+#include <object.h>
+
+#include "ceeload.h"
+
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "classnames.h"
+#include "system.h"
+#include "string.h"
+#include "sstring.h"
+#include "eeconfig.h"
+#include "assemblynative.hpp"
+#include "generics.h"
+#include "invokeutil.h"
+#include "array.h"
+#include "eepolicy.h"
+
+#if !defined(FEATURE_CORECLR)
+#include "metahost.h"
+#endif // !FEATURE_CORECLR
+
+#ifdef FEATURE_WINDOWSPHONE
+Volatile<BOOL> g_fGetPhoneVersionInitialized;
+
+// This is the API to query the phone version information
+typedef BOOL (*pfnGetPhoneVersion)(LPOSVERSIONINFO lpVersionInformation);
+
+pfnGetPhoneVersion g_pfnGetPhoneVersion = NULL;
+#endif
+
+
+FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime)
+{
+ FCALL_CONTRACT;
+
+ INT64 timestamp;
+
+ ::GetSystemTimeAsFileTime((FILETIME*)&timestamp);
+
+#if BIGENDIAN
+ timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32));
+#endif
+
+ return timestamp;
+}
+FCIMPLEND;
+
+
+
+FCIMPL0(UINT32, SystemNative::GetTickCount)
+{
+ FCALL_CONTRACT;
+
+ return ::GetTickCount();
+}
+FCIMPLEND;
+
+
+
+#ifndef FEATURE_CORECLR
+INT64 QCALLTYPE SystemNative::GetWorkingSet()
+{
+ QCALL_CONTRACT;
+
+ DWORD memUsage = 0;
+
+ BEGIN_QCALL;
+ memUsage = WszGetWorkingSet();
+ END_QCALL;
+
+ return memUsage;
+}
+#endif // !FEATURE_CORECLR
+
+VOID QCALLTYPE SystemNative::Exit(INT32 exitcode)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ // The exit code for the process is communicated in one of two ways. If the
+ // entrypoint returns an 'int' we take that. Otherwise we take a latched
+ // process exit code. This can be modified by the app via setting
+ // Environment's ExitCode property.
+ SetLatchedExitCode(exitcode);
+
+ ForceEEShutdown();
+
+ END_QCALL;
+}
+
+FCIMPL1(VOID,SystemNative::SetExitCode,INT32 exitcode)
+{
+ FCALL_CONTRACT;
+
+ // The exit code for the process is communicated in one of two ways. If the
+ // entrypoint returns an 'int' we take that. Otherwise we take a latched
+ // process exit code. This can be modified by the app via setting
+ // Environment's ExitCode property.
+ SetLatchedExitCode(exitcode);
+}
+FCIMPLEND
+
+FCIMPL0(INT32, SystemNative::GetExitCode)
+{
+ FCALL_CONTRACT;
+
+ // Return whatever has been latched so far. This is uninitialized to 0.
+ return GetLatchedExitCode();
+}
+FCIMPLEND
+
+void QCALLTYPE SystemNative::_GetCommandLine(QCall::StringHandleOnStack retString)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ LPCWSTR commandLine;
+
+ if (g_pCachedCommandLine != NULL)
+ {
+ // Use the cached command line if available
+ commandLine = g_pCachedCommandLine;
+ }
+ else
+ {
+ commandLine = WszGetCommandLine();
+ if (commandLine==NULL)
+ COMPlusThrowOM();
+ }
+
+ retString.Set(commandLine);
+
+ END_QCALL;
+}
+
+FCIMPL0(Object*, SystemNative::GetCommandLineArgs)
+{
+ FCALL_CONTRACT;
+
+ PTRARRAYREF strArray = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(strArray);
+
+ LPWSTR commandLine;
+
+ if (g_pCachedCommandLine != NULL)
+ {
+ // Use the cached command line if available
+ commandLine = g_pCachedCommandLine;
+ }
+ else
+ {
+ commandLine = WszGetCommandLine();
+ if (commandLine==NULL)
+ COMPlusThrowOM();
+ }
+
+ DWORD numArgs = 0;
+ LPWSTR* argv = SegmentCommandLine(commandLine, &numArgs);
+ if (!argv)
+ COMPlusThrowOM();
+
+ _ASSERTE(numArgs > 0);
+
+ strArray = (PTRARRAYREF) AllocateObjectArray(numArgs, g_pStringClass);
+ // Copy each argument into new Strings.
+ for(unsigned int i=0; i<numArgs; i++)
+ {
+ STRINGREF str = StringObject::NewString(argv[i]);
+ STRINGREF * destData = ((STRINGREF*)(strArray->GetDataPtr())) + i;
+ SetObjectReference((OBJECTREF*)destData, (OBJECTREF)str, strArray->GetAppDomain());
+ }
+ delete [] argv;
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(strArray);
+}
+FCIMPLEND
+
+
+FCIMPL1(FC_BOOL_RET, SystemNative::_GetCompatibilityFlag, int flag)
+{
+ FCALL_CONTRACT;
+
+ FC_RETURN_BOOL(GetCompatibilityFlag((CompatibilityFlag)flag));
+}
+FCIMPLEND
+
+// Note: Arguments checked in IL.
+FCIMPL1(Object*, SystemNative::_GetEnvironmentVariable, StringObject* strVarUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal;
+ STRINGREF strVar;
+
+ refRetVal = NULL;
+ strVar = ObjectToSTRINGREF(strVarUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_2(refRetVal, strVar);
+
+ int len;
+
+ // Get the length of the environment variable.
+ PathString envPath; // prefix complains if pass a null ptr in, so rely on the final length parm instead
+ len = WszGetEnvironmentVariable(strVar->GetBuffer(), envPath);
+
+ if (len != 0)
+ {
+ // Allocate the string.
+ refRetVal = StringObject::NewString(len);
+
+ wcscpy_s(refRetVal->GetBuffer(), len + 1, envPath);
+
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+// Return a method info for the method were the exception was thrown
+FCIMPL1(ReflectMethodObject*, SystemNative::GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ I1ARRAYREF pArray(static_cast<I1Array *>(pStackTraceUNSAFE));
+ StackTraceArray stackArray(pArray);
+
+ if (!stackArray.Size())
+ return NULL;
+
+ // The managed stacktrace classes always returns typical method definition, so we don't need to bother providing exact instantiation.
+ // Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pElements[0].pFunc, pElements[0].pExactGenericArgsToken, pTypeHandle, &pMD);
+
+ MethodDesc* pFunc = stackArray[0].pFunc;
+
+ // Strip the instantiation to make sure that the reflection never gets a bad method desc back.
+ REFLECTMETHODREF refRet = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0()
+ pFunc = pFunc->LoadTypicalMethodDefinition();
+ refRet = pFunc->GetStubMethodInfo();
+ _ASSERTE(pFunc->IsRuntimeMethodHandle());
+
+ HELPER_METHOD_FRAME_END();
+
+ return (ReflectMethodObject*)OBJECTREFToObject(refRet);
+}
+FCIMPLEND
+
+FCIMPL0(StringObject*, SystemNative::_GetModuleFileName)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+ if (g_pCachedModuleFileName)
+ {
+ refRetVal = StringObject::NewString(g_pCachedModuleFileName);
+ }
+ else
+ {
+ PathString wszFilePathString;
+
+
+ DWORD lgth = WszGetModuleFileName(NULL, wszFilePathString);
+ if (!lgth)
+ {
+ COMPlusThrowWin32();
+ }
+
+
+ refRetVal = StringObject::NewString(wszFilePathString.GetUnicode());
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return (StringObject*)OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL0(StringObject*, SystemNative::GetDeveloperPath)
+{
+#ifdef FEATURE_FUSION
+ FCALL_CONTRACT;
+
+ STRINGREF refDevPath = NULL;
+ LPWSTR pPath = NULL;
+ DWORD lgth = 0;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refDevPath);
+
+ SystemDomain::System()->GetDevpathW(&pPath, &lgth);
+ if(lgth)
+ refDevPath = StringObject::NewString(pPath, lgth);
+
+ HELPER_METHOD_FRAME_END();
+ return (StringObject*)OBJECTREFToObject(refDevPath);
+#else
+ return NULL;
+#endif
+}
+FCIMPLEND
+
+FCIMPL0(StringObject*, SystemNative::GetRuntimeDirectory)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+ DWORD dwFile = MAX_LONGPATH+1;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+ SString wszFilePathString;
+
+ WCHAR * wszFile = wszFilePathString.OpenUnicodeBuffer(dwFile);
+ HRESULT hr = GetInternalSystemDirectory(wszFile, &dwFile);
+ wszFilePathString.CloseBuffer(dwFile);
+
+ if(FAILED(hr))
+ COMPlusThrowHR(hr);
+
+ dwFile--; // remove the trailing NULL
+
+ if(dwFile)
+ refRetVal = StringObject::NewString(wszFile, dwFile);
+
+ HELPER_METHOD_FRAME_END();
+ return (StringObject*)OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL0(StringObject*, SystemNative::GetHostBindingFile);
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+
+ LPCWSTR wszFile = g_pConfig->GetProcessBindingFile();
+ if(wszFile)
+ refRetVal = StringObject::NewString(wszFile);
+
+ HELPER_METHOD_FRAME_END();
+ return (StringObject*)OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+#ifndef FEATURE_CORECLR
+
+void QCALLTYPE SystemNative::_GetSystemVersion(QCall::StringHandleOnStack retVer)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ WCHAR wszVersion[_MAX_PATH];
+ DWORD dwVersion = _MAX_PATH;
+
+ // Get the version
+ IfFailThrow(g_pCLRRuntime->GetVersionString(wszVersion, &dwVersion));
+ retVer.Set(wszVersion);
+
+ END_QCALL;
+}
+
+#endif
+
+INT32 QCALLTYPE SystemNative::GetProcessorCount()
+{
+ QCALL_CONTRACT;
+
+ INT32 processorCount = 0;
+
+ BEGIN_QCALL;
+
+ CPUGroupInfo::EnsureInitialized();
+
+ if(CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
+ {
+ processorCount = CPUGroupInfo::GetNumActiveProcessors();
+ }
+
+ // Processor count will be 0 if CPU groups are disabled/not supported
+ if(processorCount == 0)
+ {
+ SYSTEM_INFO systemInfo;
+ ZeroMemory(&systemInfo, sizeof(systemInfo));
+
+ GetSystemInfo(&systemInfo);
+
+ processorCount = systemInfo.dwNumberOfProcessors;
+ }
+
+ END_QCALL;
+
+ return processorCount;
+}
+
+#ifdef FEATURE_CLASSIC_COMINTEROP
+
+LPVOID QCALLTYPE SystemNative::GetRuntimeInterfaceImpl(
+ /*in*/ REFCLSID clsid,
+ /*in*/ REFIID riid)
+{
+ QCALL_CONTRACT;
+
+ LPVOID pUnk = NULL;
+
+ BEGIN_QCALL;
+
+#ifdef FEATURE_CORECLR
+ IfFailThrow(E_NOINTERFACE);
+#else
+ HRESULT hr = g_pCLRRuntime->GetInterface(clsid, riid, &pUnk);
+
+ if (FAILED(hr))
+ hr = g_pCLRRuntime->QueryInterface(riid, &pUnk);
+
+ IfFailThrow(hr);
+#endif
+
+ END_QCALL;
+
+ return pUnk;
+}
+
+#endif
+
+FCIMPL0(FC_BOOL_RET, SystemNative::HasShutdownStarted)
+{
+ FCALL_CONTRACT;
+
+ // Return true if the EE has started to shutdown and is now going to
+ // aggressively finalize objects referred to by static variables OR
+ // if someone is unloading the current AppDomain AND we have started
+ // finalizing objects referred to by static variables.
+ FC_RETURN_BOOL((g_fEEShutDown & ShutDown_Finalize2) || GetAppDomain()->IsFinalizing());
+}
+FCIMPLEND
+
+// FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown.
+//
+// Static message buffer used by SystemNative::FailFast to avoid reliance on a
+// managed string object buffer. This buffer is not always used, see comments in
+// the method below.
+WCHAR g_szFailFastBuffer[256];
+#define FAIL_FAST_STATIC_BUFFER_LENGTH (sizeof(g_szFailFastBuffer) / sizeof(WCHAR))
+
+// This is the common code for FailFast processing that is wrapped by the two
+// FailFast FCalls below.
+void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }CONTRACTL_END;
+
+ struct
+ {
+ STRINGREF refMesgString;
+ EXCEPTIONREF refExceptionForWatsonBucketing;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+
+ gc.refMesgString = refMesgString;
+ gc.refExceptionForWatsonBucketing = refExceptionForWatsonBucketing;
+
+ // Managed code injected FailFast maps onto the unmanaged version
+ // (EEPolicy::HandleFatalError) in the following manner: the exit code is
+ // always set to COR_E_FAILFAST and the address passed (usually a failing
+ // EIP) is in fact the address of a unicode message buffer (explaining the
+ // reason for the fault).
+ // The message string comes from a managed string object so we can't rely on
+ // the buffer remaining in place below our feet. But equally we don't want
+ // to inject failure points (by, for example, allocating a heap buffer or a
+ // pinning handle) when we have a much higher chance than usual of actually
+ // tripping those failure points and eradicating useful debugging info.
+ // We employ various strategies to deal with this:
+ // o If the message is small enough we copy it into a static buffer
+ // (g_szFailFastBuffer).
+ // o Otherwise we try to allocate a buffer of the required size on the
+ // heap. This buffer will be leaked.
+ // o If the allocation above fails we return to the static buffer and
+ // truncate the message.
+ //
+ // Another option would seem to be to implement a new frame type that
+ // protects object references as pinned, but that seems like overkill for
+ // just this problem.
+ WCHAR *pszMessage = NULL;
+ DWORD cchMessage = (gc.refMesgString == NULL) ? 0 : gc.refMesgString->GetStringLength();
+
+ if (cchMessage < FAIL_FAST_STATIC_BUFFER_LENGTH)
+ {
+ pszMessage = g_szFailFastBuffer;
+ }
+ else
+ {
+ // We can fail here, but we can handle the fault.
+ CONTRACT_VIOLATION(FaultViolation);
+ pszMessage = new (nothrow) WCHAR[cchMessage + 1];
+ if (pszMessage == NULL)
+ {
+ // Truncate the message to what will fit in the static buffer.
+ cchMessage = FAIL_FAST_STATIC_BUFFER_LENGTH - 1;
+ pszMessage = g_szFailFastBuffer;
+ }
+ }
+
+ if (cchMessage > 0)
+ memcpyNoGCRefs(pszMessage, gc.refMesgString->GetBuffer(), cchMessage * sizeof(WCHAR));
+ pszMessage[cchMessage] = W('\0');
+
+ if (cchMessage == 0) {
+ WszOutputDebugString(W("CLR: Managed code called FailFast without specifying a reason.\r\n"));
+ }
+ else {
+ WszOutputDebugString(W("CLR: Managed code called FailFast, saying \""));
+ WszOutputDebugString(pszMessage);
+ WszOutputDebugString(W("\"\r\n"));
+ }
+
+ Thread *pThread = GetThread();
+
+#ifndef FEATURE_PAL
+ // If we have the exception object, then try to setup
+ // the watson bucket if it has any details.
+#ifdef FEATURE_CORECLR
+ // On CoreCLR, Watson may not be enabled. Thus, we should
+ // skip this, if required.
+ if (IsWatsonEnabled())
+#endif // FEATURE_CORECLR
+ {
+ BEGIN_SO_INTOLERANT_CODE(pThread);
+ if ((gc.refExceptionForWatsonBucketing == NULL) || !SetupWatsonBucketsForFailFast(gc.refExceptionForWatsonBucketing))
+ {
+ PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
+ _ASSERTE(pUEWatsonBucketTracker != NULL);
+ pUEWatsonBucketTracker->SaveIpForWatsonBucket(retAddress);
+ pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL);
+ if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
+ {
+ pUEWatsonBucketTracker->ClearWatsonBucketDetails();
+ }
+ }
+ END_SO_INTOLERANT_CODE;
+ }
+#endif // !FEATURE_PAL
+
+ // stash the user-provided exception object. this will be used as
+ // the inner exception object to the FatalExecutionEngineException.
+ if (gc.refExceptionForWatsonBucketing != NULL)
+ pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing);
+
+ EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage);
+
+ GCPROTECT_END();
+}
+
+// Note: Do not merge this FCALL method with any other FailFast overloads.
+// Watson uses the managed FailFast method with one String for crash dump bucketization.
+FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_1(refMessage);
+
+ // The HelperMethodFrame knows how to get the return address.
+ UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
+
+ // Call the actual worker to perform failfast
+ GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL2(VOID, SystemNative::FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_1(refMessage);
+
+ // The HelperMethodFrame knows how to get the return address.
+ UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
+
+ // Call the actual worker to perform failfast
+ GenericFailFast(refMessage, NULL, retaddr, exitCode);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
+ EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException);
+
+ // The HelperMethodFrame knows how to get the return address.
+ UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
+
+ // Call the actual worker to perform failfast
+ GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+#ifndef FEATURE_CORECLR
+BOOL QCALLTYPE SystemNative::IsCLRHosted()
+{
+ QCALL_CONTRACT;
+
+ BOOL retVal = false;
+ BEGIN_QCALL;
+ retVal = (CLRHosted() & CLRHOSTED) != 0;
+ END_QCALL;
+
+ return retVal;
+}
+
+void QCALLTYPE SystemNative::TriggerCodeContractFailure(ContractFailureKind failureKind, LPCWSTR pMessage, LPCWSTR pCondition, LPCWSTR exceptionAsString)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ GCX_COOP();
+
+ EEPolicy::HandleCodeContractFailure(pMessage, pCondition, exceptionAsString);
+ // Note: if the host chose to throw an exception, we've returned from this method and
+ // will throw that exception in managed code, because it's easier to pass the right parameters there.
+
+ END_QCALL;
+}
+#endif // !FEATURE_CORECLR
+
+FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC)
+{
+ FCALL_CONTRACT;
+
+ FC_RETURN_BOOL(GCHeap::IsServerHeap());
+}
+FCIMPLEND
+
+#ifdef FEATURE_COMINTEROP
+
+BOOL QCALLTYPE SystemNative::WinRTSupported()
+{
+ QCALL_CONTRACT;
+
+ BOOL hasWinRT = FALSE;
+
+ BEGIN_QCALL;
+ hasWinRT = ::WinRTSupported();
+ END_QCALL;
+
+ return hasWinRT;
+}
+
+#endif // FEATURE_COMINTEROP
+
+// Helper method to retrieve OS Version based on the environment.
+BOOL GetOSVersionForEnvironment(LPOSVERSIONINFO lpVersionInformation)
+{
+#ifdef FEATURE_WINDOWSPHONE
+ // Return phone version information if it is available
+ if (!g_fGetPhoneVersionInitialized)
+ {
+ HMODULE hPhoneInfo = WszLoadLibrary(W("phoneinfo.dll"));
+ if(hPhoneInfo != NULL)
+ g_pfnGetPhoneVersion = (pfnGetPhoneVersion)GetProcAddress(hPhoneInfo, "GetPhoneVersion");
+
+ g_fGetPhoneVersionInitialized = true;
+ }
+
+ if (g_pfnGetPhoneVersion!= NULL)
+ return g_pfnGetPhoneVersion(lpVersionInformation);
+#endif // FEATURE_WINDOWSPHONE
+
+ return ::GetOSVersion(lpVersionInformation);
+}
+
+
+/*
+ * SystemNative::GetOSVersion - Fcall corresponding to System.Environment.GetVersion
+ * It calls clr!GetOSVersion to get the real OS version even when running in
+ * app compat. Calling kernel32!GetVersionEx() directly will be shimmed and will return the
+ * fake OS version. In order to avoid this the call to getVersionEx is made via mscoree.dll.
+ * Mscoree.dll resides in system32 dir and is never lied about OS version.
+ */
+
+FCIMPL1(FC_BOOL_RET, SystemNative::GetOSVersion, OSVERSIONINFOObject *osVer)
+{
+ FCALL_CONTRACT;
+
+ OSVERSIONINFO ver;
+ ver.dwOSVersionInfoSize = osVer->dwOSVersionInfoSize;
+
+ BOOL ret = GetOSVersionForEnvironment(&ver);
+
+ if(ret)
+ {
+ osVer->dwMajorVersion = ver.dwMajorVersion;
+ osVer->dwMinorVersion = ver.dwMinorVersion;
+ osVer->dwBuildNumber = ver.dwBuildNumber;
+ osVer->dwPlatformId = ver.dwPlatformId;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(osVer);
+ SetObjectReference((OBJECTREF*)&(osVer->szCSDVersion), StringObject::NewString(ver.szCSDVersion), GetAppDomain());
+ HELPER_METHOD_FRAME_END();
+ }
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+/*
+ * SystemNative::GetOSVersionEx - Fcall implementation for System.Environment.GetVersionEx
+ * Similar as above except this takes OSVERSIONINFOEX structure as input
+ */
+
+FCIMPL1(FC_BOOL_RET, SystemNative::GetOSVersionEx, OSVERSIONINFOEXObject *osVer)
+{
+ FCALL_CONTRACT;
+
+ OSVERSIONINFOEX ver;
+ ver.dwOSVersionInfoSize = osVer->dwOSVersionInfoSize;
+
+ BOOL ret = GetOSVersionForEnvironment((OSVERSIONINFO *)&ver);
+
+ if(ret)
+ {
+ osVer->dwMajorVersion = ver.dwMajorVersion;
+ osVer->dwMinorVersion = ver.dwMinorVersion;
+ osVer->dwBuildNumber = ver.dwBuildNumber;
+ osVer->dwPlatformId = ver.dwPlatformId;
+ osVer->wServicePackMajor = ver.wServicePackMajor;
+ osVer->wServicePackMinor = ver.wServicePackMinor;
+ osVer->wSuiteMask = ver.wSuiteMask;
+ osVer->wProductType = ver.wProductType;
+ osVer->wReserved = ver.wReserved;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(osVer);
+ SetObjectReference((OBJECTREF*)&(osVer->szCSDVersion), StringObject::NewString(ver.szCSDVersion), GetAppDomain());
+ HELPER_METHOD_FRAME_END();
+ }
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+
+#ifndef FEATURE_CORECLR
+//
+// SystemNative::LegacyFormatMode - Fcall implementation for System.TimeSpan.LegacyFormatMode
+// checks for the DWORD "TimeSpan_LegacyFormatMode" CLR config option
+//
+FCIMPL0(FC_BOOL_RET, SystemNative::LegacyFormatMode)
+{
+ FCALL_CONTRACT;
+
+ DWORD flag = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
+ flag = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_TimeSpan_LegacyFormatMode);
+ END_SO_INTOLERANT_CODE;
+
+ if (flag)
+ FC_RETURN_BOOL(TRUE);
+ else
+ FC_RETURN_BOOL(FALSE);
+}
+FCIMPLEND
+#endif // !FEATURE_CORECLR
+
+
+#ifndef FEATURE_CORECLR
+//
+// SystemNative::CheckLegacyManagedDeflateStream - Fcall implementation for System.IO.Compression.DeflateStream
+// checks for the DWORD "NetFx45_LegacyManagedDeflateStream" CLR config option
+//
+// Move this into a separate CLRConfigQCallWrapper class once CLRConfig has been refactored!
+//
+FCIMPL0(FC_BOOL_RET, SystemNative::CheckLegacyManagedDeflateStream)
+{
+ FCALL_CONTRACT;
+
+ DWORD flag = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
+ flag = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_LegacyManagedDeflateStream);
+ END_SO_INTOLERANT_CODE;
+
+ if (flag)
+ FC_RETURN_BOOL(TRUE);
+ else
+ FC_RETURN_BOOL(FALSE);
+}
+FCIMPLEND
+#endif // !FEATURE_CORECLR
+
+#ifndef FEATURE_CORECLR
+//
+// SystemNative::CheckThrowUnobservedTaskExceptions - Fcall implementation for System.Threading.Tasks.TaskExceptionHolder
+// checks for the DWORD "ThrowUnobservedTaskExceptions" CLR config option
+//
+FCIMPL0(FC_BOOL_RET, SystemNative::CheckThrowUnobservedTaskExceptions)
+{
+ FCALL_CONTRACT;
+
+ DWORD flag = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
+ flag = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ThrowUnobservedTaskExceptions);
+ END_SO_INTOLERANT_CODE;
+
+ if (flag)
+ FC_RETURN_BOOL(TRUE);
+ else
+ FC_RETURN_BOOL(FALSE);
+}
+FCIMPLEND
+
+BOOL QCALLTYPE SystemNative::LegacyDateTimeParseMode()
+{
+ QCALL_CONTRACT;
+
+ BOOL retVal = false;
+ BEGIN_QCALL;
+ retVal = (BOOL) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DateTime_NetFX35ParseMode);
+ END_QCALL;
+
+ return retVal;
+}
+
+//
+// This method used with DateTimeParse to fix the parsing of AM/PM like "1/10 5 AM" case
+//
+BOOL QCALLTYPE SystemNative::EnableAmPmParseAdjustment()
+{
+ QCALL_CONTRACT;
+
+ BOOL retVal = false;
+ BEGIN_QCALL;
+ retVal = (BOOL) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DateTime_NetFX40AmPmParseAdjustment);
+ END_QCALL;
+
+ return retVal;
+}
+
+
+#endif // !FEATURE_CORECLR
+
diff --git a/src/classlibnative/bcltype/system.h b/src/classlibnative/bcltype/system.h
new file mode 100644
index 0000000000..60355b3388
--- /dev/null
+++ b/src/classlibnative/bcltype/system.h
@@ -0,0 +1,158 @@
+// 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.
+//
+// File: System.h
+//
+
+//
+// Purpose: Native methods on System.System
+//
+
+//
+
+#ifndef _SYSTEM_H_
+#define _SYSTEM_H_
+
+#include "fcall.h"
+#include "qcall.h"
+
+// Corresponding to managed class Microsoft.Win32.OSVERSIONINFO
+class OSVERSIONINFOObject : public Object
+{
+ public:
+ STRINGREF szCSDVersion;
+ DWORD dwOSVersionInfoSize;
+ DWORD dwMajorVersion;
+ DWORD dwMinorVersion;
+ DWORD dwBuildNumber;
+ DWORD dwPlatformId;
+};
+
+//Corresponding to managed class Microsoft.Win32.OSVERSIONINFOEX
+class OSVERSIONINFOEXObject : public Object
+{
+ public:
+ STRINGREF szCSDVersion;
+ DWORD dwOSVersionInfoSize;
+ DWORD dwMajorVersion;
+ DWORD dwMinorVersion;
+ DWORD dwBuildNumber;
+ DWORD dwPlatformId;
+ WORD wServicePackMajor;
+ WORD wServicePackMinor;
+ WORD wSuiteMask;
+ BYTE wProductType;
+ BYTE wReserved;
+};
+
+
+
+class SystemNative
+{
+ friend class DebugStackTrace;
+
+private:
+ struct CaptureStackTraceData
+ {
+ // Used for the integer-skip version
+ INT32 skip;
+
+ INT32 cElementsAllocated;
+ INT32 cElements;
+ StackTraceElement* pElements;
+ void* pStopStack; // use to limit the crawl
+
+ CaptureStackTraceData() : skip(0), cElementsAllocated(0), cElements(0), pElements(NULL), pStopStack((void*)-1)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+ };
+
+public:
+ // Functions on the System.Environment class
+ static FCDECL0(INT64, __GetSystemTimeAsFileTime);
+ static FCDECL0(UINT32, GetTickCount);
+ static FCDECL1(FC_BOOL_RET, GetOSVersion, OSVERSIONINFOObject *osVer);
+ static FCDECL1(FC_BOOL_RET, GetOSVersionEx, OSVERSIONINFOEXObject *osVer);
+
+#ifndef FEATURE_CORECLR
+ static
+ INT64 QCALLTYPE GetWorkingSet();
+#endif // !FEATURE_CORECLR
+
+ static
+ void QCALLTYPE Exit(INT32 exitcode);
+
+ static FCDECL1(VOID,SetExitCode,INT32 exitcode);
+ static FCDECL0(INT32, GetExitCode);
+
+ static
+ void QCALLTYPE _GetCommandLine(QCall::StringHandleOnStack retString);
+
+ static FCDECL0(Object*, GetCommandLineArgs);
+ static FCDECL1(FC_BOOL_RET, _GetCompatibilityFlag, int flag);
+ static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
+ static FCDECL2(VOID, FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode);
+ static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
+#ifndef FEATURE_CORECLR
+ static void QCALLTYPE TriggerCodeContractFailure(ContractFailureKind failureKind, LPCWSTR pMessage, LPCWSTR pCondition, LPCWSTR exceptionAsText);
+ static BOOL QCALLTYPE IsCLRHosted();
+#endif // !FEATURE_CORECLR
+
+ static FCDECL0(StringObject*, GetDeveloperPath);
+ static FCDECL1(Object*, _GetEnvironmentVariable, StringObject* strVar);
+ static FCDECL0(StringObject*, _GetModuleFileName);
+ static FCDECL0(StringObject*, GetRuntimeDirectory);
+ static FCDECL0(StringObject*, GetHostBindingFile);
+ static LPVOID QCALLTYPE GetRuntimeInterfaceImpl(REFCLSID clsid, REFIID riid);
+ static void QCALLTYPE _GetSystemVersion(QCall::StringHandleOnStack retVer);
+
+ // Returns the number of logical processors that can be used by managed code
+ static INT32 QCALLTYPE GetProcessorCount();
+
+ static FCDECL0(FC_BOOL_RET, HasShutdownStarted);
+ static FCDECL0(FC_BOOL_RET, IsServerGC);
+
+#ifdef FEATURE_COMINTEROP
+ static
+ BOOL QCALLTYPE WinRTSupported();
+#endif // FEATURE_COMINTEROP
+
+ // Return a method info for the method were the exception was thrown
+ static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE);
+
+#ifndef FEATURE_CORECLR
+ // Functions on the System.TimeSpan class
+ static FCDECL0(FC_BOOL_RET, LegacyFormatMode);
+ // Function on the DateTime
+ static BOOL QCALLTYPE EnableAmPmParseAdjustment();
+ static BOOL QCALLTYPE LegacyDateTimeParseMode();
+#endif // !FEATURE_CORECLR
+
+
+// Move this into a separate CLRConfigQCallWrapper class once CLRConfif has been refactored:
+#ifndef FEATURE_CORECLR
+ static FCDECL0(FC_BOOL_RET, CheckLegacyManagedDeflateStream);
+#endif // !FEATURE_CORECLR
+
+#ifndef FEATURE_CORECLR
+ static FCDECL0(FC_BOOL_RET, CheckThrowUnobservedTaskExceptions);
+#endif // !FEATURE_CORECLR
+
+private:
+ // Common processing code for FailFast
+ static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode);
+};
+
+/* static */
+void QCALLTYPE GetTypeLoadExceptionMessage(UINT32 resId, QCall::StringHandleOnStack retString);
+
+/* static */
+void QCALLTYPE GetFileLoadExceptionMessage(UINT32 hr, QCall::StringHandleOnStack retString);
+
+/* static */
+void QCALLTYPE FileLoadException_GetMessageForHR(UINT32 hresult, QCall::StringHandleOnStack retString);
+
+#endif // _SYSTEM_H_
+
diff --git a/src/classlibnative/bcltype/varargsnative.cpp b/src/classlibnative/bcltype/varargsnative.cpp
new file mode 100644
index 0000000000..33dd816477
--- /dev/null
+++ b/src/classlibnative/bcltype/varargsnative.cpp
@@ -0,0 +1,638 @@
+// 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.
+//
+// File: VarArgsNative.cpp
+//
+
+//
+// This module contains the implementation of the native methods for the
+// varargs class(es)..
+//
+
+
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "varargsnative.h"
+
+// Some platforms have additional alignment requirements for arguments. This function adjusts the given arg
+// pointer to achieve such an alignment for the next argument on those platforms (otherwise it is a no-op).
+// NOTE: the debugger has its own implementation of this algorithm in Debug\DI\RsType.cpp, CordbType::RequiresAlign8()
+// so if you change this implementation be sure to update the debugger's version as well.
+static void AdjustArgPtrForAlignment(VARARGS *pData, size_t cbArg)
+{
+#ifdef _TARGET_ARM_
+ // Only 64-bit primitives or value types with embedded 64-bit primitives are aligned on 64-bit boundaries.
+ if (cbArg < 8)
+ return;
+
+ // For the value type case we have to dig deeper and ask the typeloader whether the type requires
+ // alignment.
+ SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic.
+ CorElementType et = pData->SigPtr.PeekElemTypeClosed(pData->ArgCookie->pModule, &typeContext);
+ if (et == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ return;
+ }
+ else
+ if (et == ELEMENT_TYPE_VALUETYPE)
+ {
+ SigPointer tempSig(pData->SigPtr);
+ TypeHandle valueType = tempSig.GetTypeHandleThrowing(pData->ArgCookie->pModule, &typeContext);
+ if (!valueType.AsMethodTable()->RequiresAlign8())
+ return;
+ }
+ else
+ {
+ // One of the primitive 64-bit types
+ }
+ pData->ArgPtr = (BYTE*)ALIGN_UP(pData->ArgPtr, 8);
+#endif // _TARGET_ARM_
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Initialize the basic info for processing a varargs parameter list.
+////////////////////////////////////////////////////////////////////////////////
+static void InitCommon(VARARGS *data, VASigCookie** cookie)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(data));
+ PRECONDITION(CheckPointer(cookie));
+ } CONTRACTL_END;
+
+ // Save the cookie and a copy of the signature.
+ data->ArgCookie = *cookie;
+ data->SigPtr = data->ArgCookie->signature.CreateSigPointer();
+
+ // Skip the calling convention, get the # of args and skip the return type.
+ ULONG callConv;
+ IfFailThrow(data->SigPtr.GetCallingConvInfo(&callConv));
+
+ ULONG sigData;
+ IfFailThrow(data->SigPtr.GetData(&sigData));
+ data->RemainingArgs = sigData;
+
+ IfFailThrow(data->SigPtr.SkipExactlyOne());
+
+ // Get a pointer to the cookie arg.
+ data->ArgPtr = (BYTE *) cookie;
+
+#if defined(_TARGET_X86_)
+ // STACK_GROWS_DOWN_ON_ARGS_WALK
+
+ // <return address> ;; lower memory
+ // <varargs_cookie> \
+ // <argN> \
+ // += sizeOfArgs
+ // /
+ // <arg1> /
+ // * <this> ;; if an instance method (note: <this> is usally passed in
+ // ;; a register and wouldn't appear on the stack frame)
+ // ;; higher memory
+ //
+ // When the stack grows down, ArgPtr always points to the end of the next
+ // argument passed. So we initialize it to the address that is the
+ // end of the first fixed arg (arg1) (marked with a '*').
+
+ data->ArgPtr += data->ArgCookie->sizeOfArgs;
+#else
+ // STACK_GROWS_UP_ON_ARGS_WALK
+
+ // <this> ;; lower memory
+ // <varargs_cookie> ;; if an instance method
+ // * <arg1>
+ //
+ // <argN> ;; higher memory
+ //
+ // When the stack grows up, ArgPtr always points to the start of the next
+ // argument passed. So we initialize it to the address marked with a '*',
+ // which is the start of the first fixed arg (arg1).
+
+ // Always skip over the varargs_cookie.
+ data->ArgPtr += StackElemSize(sizeof(LPVOID));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// After initialization advance the next argument pointer to the first optional
+// argument.
+////////////////////////////////////////////////////////////////////////////////
+void AdvanceArgPtr(VARARGS *data)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(data));
+ } CONTRACTL_END;
+
+ // Advance to the first optional arg.
+ while (data->RemainingArgs > 0)
+ {
+ if (data->SigPtr.AtSentinel())
+ break;
+
+ SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic
+ SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule, &typeContext);
+ SIZE_T cbArg = StackElemSize(cbRaw);
+
+#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
+ if (ArgIterator::IsVarArgPassedByRef(cbRaw))
+ cbArg = sizeof(void*);
+#endif
+
+ // Adjust the frame pointer and the signature info.
+ AdjustArgPtrForAlignment(data, cbArg);
+#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK
+ data->ArgPtr -= cbArg;
+#else // STACK_GROWS_UP_ON_ARGS_WALK
+ data->ArgPtr += cbArg;
+#endif // STACK_GROWS_**_ON_ARGS_WALK
+
+ IfFailThrow(data->SigPtr.SkipExactlyOne());
+ --data->RemainingArgs;
+ }
+} // AdvanceArgPtr
+
+////////////////////////////////////////////////////////////////////////////////
+// ArgIterator constructor that initializes the state to support iteration
+// of the args starting at the first optional argument.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL2(void, VarArgsNative::Init, VARARGS* _this, LPVOID cookie)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ _ASSERTE(_this != NULL);
+ VARARGS* data = _this;
+ if (cookie == 0)
+ COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized"));
+
+ VASigCookie* pCookie = *(VASigCookie**)(cookie);
+
+ if (pCookie->signature.IsEmpty())
+ {
+ data->SigPtr = SigPointer(NULL, 0);
+ data->ArgCookie = NULL;
+ data->ArgPtr = (BYTE*)((VASigCookieEx*)pCookie)->m_pArgs;
+ }
+ else
+ {
+ // Use common code to pick the cookie apart and advance to the ...
+ InitCommon(data, (VASigCookie**)cookie);
+ AdvanceArgPtr(data);
+ }
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+////////////////////////////////////////////////////////////////////////////////
+// ArgIterator constructor that initializes the state to support iteration
+// of the args starting at the argument following the supplied argument pointer.
+// Specifying NULL as the firstArg parameter causes it to start at the first
+// argument to the call.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL3(
+void,
+VarArgsNative::Init2,
+ VARARGS * _this,
+ LPVOID cookie,
+ LPVOID firstArg)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_0();
+
+ _ASSERTE(_this != NULL);
+ VARARGS* data = _this;
+ if (cookie == 0)
+ COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized"));
+
+ // Init most of the structure.
+ InitCommon(data, (VASigCookie**)cookie);
+
+ // If it is NULL, start at the first arg.
+ if (firstArg != NULL)
+ {
+ //
+ // The expectation made by VarArgsNative is that:
+#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK
+ // data->ArgPtr points to the end of the next argument.
+ // <varargs_cookie>
+ // <argN> <-- data->ArgPtr after InitCommon
+ //
+ // <argM 1st optional arg>
+ // *@ <arg2> <-- firstArg, data->ArgPtr leaving Init2()
+ // <arg1>
+ // <this> ;; if an instance method
+ // ;; higher memory
+ //
+#else // STACK_GROWS_UP_ON_ARGS_WALK
+ // data->ArgPtr points to the beginning of the next argument
+ // <this> ;; if an instance method
+ // <varargs_cookie>
+ // <arg1> <-- data->ArgPtr after InitCommon
+ // * <arg2> <-- firstArg
+ // @ <argM - 1st optional arg> <-- data->ArgPtr leaving Init2()
+ //
+ // <argN>
+ // ;; higher memory
+#endif // STACK_GROWS_**_ON_ARGS_WALK
+ // where * indicates the place on the stack that firstArg points upon
+ // entry to Init2. We need to correct in the STACK_GROWS_UP... case since
+ // we actually want to point at the argument after firstArg. This confusion
+ // comes from the difference in expectation of whether ArgPtr is pointing
+ // at the beginning or end of the argument on the stack.
+ //
+ // @ indicates where we want data->ArgPtr to point to after we're done with Init2
+ //
+
+ // Advance to the specified arg.
+ while (data->RemainingArgs > 0)
+ {
+ if (data->SigPtr.AtSentinel())
+ {
+ COMPlusThrow(kArgumentException);
+ }
+
+ SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic
+ SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext);
+ SIZE_T cbArg = StackElemSize(cbRaw);
+
+#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
+ if (ArgIterator::IsVarArgPassedByRef(cbRaw))
+ cbArg = sizeof(void*);
+#endif
+
+ // Adjust the frame pointer and the signature info.
+ AdjustArgPtrForAlignment(data, cbArg);
+ IfFailThrow(data->SigPtr.SkipExactlyOne());
+ data->RemainingArgs--;
+
+#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK
+ data->ArgPtr -= cbArg;
+ bool atFirstArg = (data->ArgPtr == firstArg);
+#else // STACK_GROWS_UP_ON_ARGS_WALK
+ bool atFirstArg = (data->ArgPtr == firstArg);
+ data->ArgPtr += cbArg;
+#endif // STACK_GROWS_**_ON_ARGS_WALK
+
+ if (atFirstArg)
+ break;
+ }
+ }
+ HELPER_METHOD_FRAME_END();
+} // VarArgsNative::Init2
+FCIMPLEND
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Return the number of unprocessed args in the argument iterator.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL1(int, VarArgsNative::GetRemainingCount, VARARGS* _this)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ _ASSERTE(_this != NULL);
+ if (!(_this->ArgCookie))
+ {
+ // this argiterator was created by marshaling from an unmanaged va_list -
+ // can't do this operation
+ COMPlusThrow(kNotSupportedException);
+ }
+ HELPER_METHOD_FRAME_END();
+ return (_this->RemainingArgs);
+}
+FCIMPLEND
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Retrieve the type of the next argument without consuming it.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL1(void*, VarArgsNative::GetNextArgType, VARARGS* _this)
+{
+ FCALL_CONTRACT;
+
+ TypedByRef value;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ PREFIX_ASSUME(_this != NULL);
+ VARARGS data = *_this;
+
+ if (!(_this->ArgCookie))
+ {
+ // this argiterator was created by marshaling from an unmanaged va_list -
+ // can't do this operation
+ COMPlusThrow(kNotSupportedException);
+ }
+
+
+ // Make sure there are remaining args.
+ if (data.RemainingArgs == 0)
+ COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded"));
+
+ GetNextArgHelper(&data, &value, FALSE);
+ HELPER_METHOD_FRAME_END();
+ return value.type.AsPtr();
+}
+FCIMPLEND
+
+////////////////////////////////////////////////////////////////////////////////
+// Retrieve the next argument and return it in a TypedByRef and advance the
+// next argument pointer.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL2(void, VarArgsNative::DoGetNextArg, VARARGS* _this, void * value)
+{
+ FCALL_CONTRACT;
+
+ TypedByRef * result = (TypedByRef *)value;
+ HELPER_METHOD_FRAME_BEGIN_0();
+ GCPROTECT_BEGININTERIOR (result);
+
+ _ASSERTE(_this != NULL);
+ if (!(_this->ArgCookie))
+ {
+ // this argiterator was created by marshaling from an unmanaged va_list -
+ // can't do this operation
+ COMPlusThrow(kInvalidOperationException);
+ }
+
+ // Make sure there are remaining args.
+ if (_this->RemainingArgs == 0)
+ COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded"));
+
+ GetNextArgHelper(_this, result, TRUE);
+ GCPROTECT_END ();
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Retrieve the next argument and return it in a TypedByRef and advance the
+// next argument pointer.
+////////////////////////////////////////////////////////////////////////////////
+FCIMPL3(void, VarArgsNative::GetNextArg2, VARARGS* _this, void * value, ReflectClassBaseObject *pTypeUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ TypedByRef * result = (TypedByRef *)value;
+ REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
+
+ if (refType == NULL)
+ FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle"));
+
+ HELPER_METHOD_FRAME_BEGIN_1(refType);
+ GCPROTECT_BEGININTERIOR (result);
+
+ // IJW
+
+ TypeHandle typehandle = refType->GetType();
+
+ _ASSERTE(_this != NULL);
+ UINT size = 0;
+
+ CorElementType typ = typehandle.GetInternalCorElementType();
+ if (CorTypeInfo::IsPrimitiveType(typ))
+ {
+ size = CorTypeInfo::Size(typ);
+ }
+ else if (typ == ELEMENT_TYPE_PTR)
+ {
+ size = sizeof(LPVOID);
+ }
+ else if (typ == ELEMENT_TYPE_VALUETYPE)
+ {
+ size = typehandle.AsMethodTable()->GetNativeSize();
+ }
+ else
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+ }
+
+ size = StackElemSize(size);
+
+ AdjustArgPtrForAlignment(_this, size);
+
+#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
+ if (ArgIterator::IsVarArgPassedByRef(size))
+ {
+ result->data = *(void**)_this->ArgPtr;
+ size = sizeof(void*);
+ }
+ else
+#endif
+ {
+ result->data = (void*)_this->ArgPtr;
+ }
+
+ result->type = typehandle;
+ _this->ArgPtr += size;
+
+ GCPROTECT_END ();
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This is a helper that uses a VARARGS tracking data structure to retrieve
+// the next argument out of a varargs function call. This does not check if
+// there are any args remaining (it assumes it has been checked).
+////////////////////////////////////////////////////////////////////////////////
+void
+VarArgsNative::GetNextArgHelper(
+ VARARGS * data,
+ TypedByRef * value,
+ BOOL fData)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(data));
+ PRECONDITION(CheckPointer(value));
+ } CONTRACTL_END;
+
+ GCPROTECT_BEGININTERIOR (value);
+ CorElementType elemType;
+
+ _ASSERTE(data->RemainingArgs != 0);
+
+ SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic
+ SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext);
+ SIZE_T cbArg = StackElemSize(cbRaw);
+
+ AdjustArgPtrForAlignment(data, cbArg);
+
+ // Get a pointer to the beginning of the argument.
+#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK
+ data->ArgPtr -= cbArg;
+#endif
+
+ // Assume the ref pointer points directly at the arg on the stack.
+ void* origArgPtr = data->ArgPtr;
+ value->data = origArgPtr;
+
+#ifndef STACK_GROWS_DOWN_ON_ARGS_WALK
+ data->ArgPtr += cbArg;
+#endif // STACK_GROWS_**_ON_ARGS_WALK
+
+
+TryAgain:
+ switch (elemType = data->SigPtr.PeekElemTypeClosed(data->ArgCookie->pModule, &typeContext))
+ {
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+#if BIGENDIAN
+ if ( origArgPtr == value->data ) {
+ value->data = (BYTE*)origArgPtr + (sizeof(void*)-1);
+ }
+#endif
+ value->type = MscorlibBinder::GetElementType(elemType);
+ break;
+
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_CHAR:
+#if BIGENDIAN
+ if ( origArgPtr == value->data ) {
+ value->data = (BYTE*)origArgPtr + (sizeof(void*)-2);
+ }
+#endif
+ value->type = MscorlibBinder::GetElementType(elemType);
+ break;
+
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_I:
+ case ELEMENT_TYPE_U:
+ value->type = MscorlibBinder::GetElementType(elemType);
+ break;
+
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R8:
+ value->type = MscorlibBinder::GetElementType(elemType);
+#if !defined(_WIN64) && (DATA_ALIGNMENT > 4)
+ if ( fData && origArgPtr == value->data ) {
+ // allocate an aligned copy of the value
+ value->data = value->type.GetMethodTable()->Box(origArgPtr, FALSE)->UnBox();
+ }
+#endif
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext);
+ break;
+
+ case ELEMENT_TYPE_BYREF:
+ // Check if we have already processed a by-ref.
+ if (value->data != origArgPtr)
+ {
+ _ASSERTE(!"Can't have a ByRef of a ByRef");
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+ }
+
+ // Dereference the argument to remove the indirection of the ByRef.
+ value->data = *((void **) value->data);
+
+ // Consume and discard the element type.
+ IfFailThrow(data->SigPtr.GetElemType(NULL));
+ goto TryAgain;
+
+ case ELEMENT_TYPE_VALUETYPE:
+
+#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
+ if (origArgPtr == value->data && ArgIterator::IsVarArgPassedByRef(cbRaw))
+ {
+ // Adjust the arg pointer so only one word has been skipped
+ data->ArgPtr = (BYTE *)origArgPtr + sizeof(void*);
+ // Dereference the argument because the valuetype is passed by reference
+ value->data = *((void **) origArgPtr);
+ }
+#endif
+
+#if BIGENDIAN
+ // Adjust the pointer for small valuetypes
+ if (origArgPtr == value->data) {
+ value->data = StackElemEndianessFixup(origArgPtr, cbRaw);
+ }
+#endif
+
+ // fall through
+ case ELEMENT_TYPE_CLASS: {
+ value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext);
+
+ if (value->type.GetMethodTable()->IsByRefLike())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+ }
+
+ if (elemType == ELEMENT_TYPE_CLASS && value->type.GetMethodTable()->IsValueType())
+ value->type = g_pObjectClass;
+ } break;
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ if (value->data != origArgPtr)
+ {
+ //<TODO>@todo: Is this really an error?</TODO>
+ _ASSERTE(!"Can't have a ByRef of a TypedByRef");
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+ }
+#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE
+ if (ArgIterator::IsVarArgPassedByRef(sizeof(TypedByRef)))
+ {
+ // Adjust the arg pointer so only one word has been skipped
+ data->ArgPtr = (BYTE *)origArgPtr + sizeof(void *);
+ // Dereference the argument because the valuetypes are always passed by reference
+ value->data = *((void **)origArgPtr);
+ }
+#endif // ENREGISTERED_PARAMTYPE_MAXSIZE
+ // Load the TypedByRef
+ value->type = ((TypedByRef*)value->data)->type;
+ value->data = ((TypedByRef*)value->data)->data;
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ {
+ value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule,&typeContext);
+
+ break;
+ }
+
+ case ELEMENT_TYPE_FNPTR:
+ case ELEMENT_TYPE_OBJECT:
+ _ASSERTE(!"Not implemented");
+ COMPlusThrow(kNotSupportedException);
+ break;
+
+ case ELEMENT_TYPE_SENTINEL:
+ default:
+ _ASSERTE(!"Should be unreachable");
+ COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
+ break;
+ }
+
+ // Update the tracking stuff to move past the argument.
+ --data->RemainingArgs;
+ IfFailThrow(data->SigPtr.SkipExactlyOne());
+ GCPROTECT_END ();
+} // VarArgsNative::GetNextArgHelper
diff --git a/src/classlibnative/bcltype/varargsnative.h b/src/classlibnative/bcltype/varargsnative.h
new file mode 100644
index 0000000000..c46e3dffaa
--- /dev/null
+++ b/src/classlibnative/bcltype/varargsnative.h
@@ -0,0 +1,33 @@
+// 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.
+//
+// File: VarArgsNative.h
+//
+
+//
+// This module contains the implementation of the native methods for the
+// varargs class(es)..
+//
+
+#ifndef _VARARGSNATIVE_H_
+#define _VARARGSNATIVE_H_
+
+#include "clrvarargs.h"
+
+class VarArgsNative
+{
+public:
+ static FCDECL3(void, Init2, VARARGS* _this, LPVOID cookie, LPVOID firstArg);
+ static FCDECL2(void, Init, VARARGS* _this, LPVOID cookie);
+ static FCDECL1(int, GetRemainingCount, VARARGS* _this);
+ static FCDECL1(void*, GetNextArgType, VARARGS* _this);
+ //TypedByRef can not be passed by ref, so has to pass it as void pointer
+ static FCDECL2(void, DoGetNextArg, VARARGS* _this, void * value);
+ //TypedByRef can not be passed by ref, so has to pass it as void pointer
+ static FCDECL3(void, GetNextArg2, VARARGS* _this, void * value, ReflectClassBaseObject *pTypeUNSAFE);
+
+ static void GetNextArgHelper(VARARGS *data, TypedByRef *value, BOOL fData);
+};
+
+#endif // _VARARGSNATIVE_H_
diff --git a/src/classlibnative/bcltype/variant.cpp b/src/classlibnative/bcltype/variant.cpp
new file mode 100644
index 0000000000..892c5888a6
--- /dev/null
+++ b/src/classlibnative/bcltype/variant.cpp
@@ -0,0 +1,361 @@
+// 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.
+//
+// File: Variant.cpp
+//
+
+//
+// Purpose: Native Implementation of the Variant Class
+//
+
+//
+
+#include "common.h"
+
+#ifdef FEATURE_COMINTEROP
+
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "variant.h"
+#include "string.h"
+#include "field.h"
+
+// The following values are used to represent underlying
+// type of the Enum..
+#define EnumI1 0x100000
+#define EnumU1 0x200000
+#define EnumI2 0x300000
+#define EnumU2 0x400000
+#define EnumI4 0x500000
+#define EnumU4 0x600000
+#define EnumI8 0x700000
+#define EnumU8 0x800000
+#define EnumMask 0xF00000
+
+//
+// Current Conversions
+//
+
+FCIMPL1(float, COMVariant::GetR4FromVar, VariantData* var)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ }
+ CONTRACTL_END;
+
+ INT32 val = var->GetDataAsInt32();
+ return (float&)val;
+}
+FCIMPLEND
+
+FCIMPL1(double, COMVariant::GetR8FromVar, VariantData* var)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ }
+ CONTRACTL_END;
+
+ INT64 val = var->GetDataAsInt64();
+ return (double&)val;
+}
+FCIMPLEND
+
+
+/*=================================SetFieldsR4==================================
+**
+==============================================================================*/
+FCIMPL2_IV(void, COMVariant::SetFieldsR4, VariantData* var, float val)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ }
+ CONTRACTL_END;
+
+ INT64 tempData;
+
+ tempData = *((INT32 *)(&val));
+ var->SetData(&tempData);
+ var->SetType(CV_R4);
+}
+FCIMPLEND
+
+
+/*=================================SetFieldsR8==================================
+**
+==============================================================================*/
+FCIMPL2_IV(void, COMVariant::SetFieldsR8, VariantData* var, double val)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ }
+ CONTRACTL_END;
+
+ var->SetData((void *)(&val));
+ var->SetType(CV_R8);
+}
+FCIMPLEND
+
+
+/*===============================SetFieldsObject================================
+**
+==============================================================================*/
+FCIMPL2(void, COMVariant::SetFieldsObject, VariantData* var, Object* vVal)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ PRECONDITION(CheckPointer(vVal));
+ }
+ CONTRACTL_END;
+
+ OBJECTREF val = ObjectToOBJECTREF(vVal);
+
+ HELPER_METHOD_FRAME_BEGIN_1(val);
+ GCPROTECT_BEGININTERIOR(var)
+
+ CVTypes cvt = CV_EMPTY;
+ TypeHandle typeHandle;
+
+ MethodTable *valMT = val->GetMethodTable();
+
+ //If this isn't a value class, we should just skip out because we're not going
+ //to do anything special with it.
+ if (!valMT->IsValueType())
+ {
+ var->SetObjRef(val);
+ typeHandle = TypeHandle(valMT);
+
+ if (typeHandle==GetTypeHandleForCVType(CV_MISSING))
+ {
+ var->SetType(CV_MISSING);
+ }
+ else if (typeHandle==GetTypeHandleForCVType(CV_NULL))
+ {
+ var->SetType(CV_NULL);
+ }
+ else if (typeHandle==GetTypeHandleForCVType(CV_EMPTY))
+ {
+ var->SetType(CV_EMPTY);
+ var->SetObjRef(NULL);
+ }
+ else
+ {
+ var->SetType(CV_OBJECT);
+ }
+ }
+ else if (IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl()))
+ {
+ // System.Drawing.Color is converted to UInt32
+ var->SetDataAsUInt32(ConvertSystemColorToOleColor(&val));
+ var->SetType(CV_U4);
+ }
+ else
+ {
+ //If this is a primitive type, we need to unbox it, get the value and create a variant
+ //with just those values.
+ void *UnboxData = val->UnBox();
+
+ ClearObjectReference(var->GetObjRefPtr());
+ typeHandle = TypeHandle(valMT);
+ CorElementType cet = typeHandle.GetSignatureCorElementType();
+
+ if (cet>=ELEMENT_TYPE_BOOLEAN && cet<=ELEMENT_TYPE_STRING)
+ {
+ cvt = (CVTypes)cet;
+ }
+ else
+ {
+ cvt = GetCVTypeFromClass(valMT);
+ }
+ var->SetType(cvt);
+
+
+ //copy all of the data.
+ // Copies must be done based on the exact number of bytes to copy.
+ // We don't want to read garbage from other blocks of memory.
+ //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities
+ //If we don't find one of those ranges, we've found a value class
+ //of which we don't have inherent knowledge, so just slam that into an
+ //ObjectRef.
+ if (cvt>=CV_BOOLEAN && cvt<=CV_U1 && cvt != CV_CHAR)
+ {
+ var->SetDataAsInt64(*((UINT8 *)UnboxData));
+ }
+ else if (cvt==CV_CHAR || cvt>=CV_I2 && cvt<=CV_U2)
+ {
+ var->SetDataAsInt64(*((UINT16 *)UnboxData));
+ }
+ else if (cvt>=CV_I4 && cvt<=CV_U4 || cvt==CV_R4)
+ {
+ var->SetDataAsInt64(*((UINT32 *)UnboxData));
+ }
+ else if ((cvt>=CV_I8 && cvt<=CV_R8) || (cvt==CV_DATETIME) || (cvt==CV_TIMESPAN) || (cvt==CV_CURRENCY))
+ {
+ var->SetDataAsInt64(*((INT64 *)UnboxData));
+ }
+ else if (cvt==CV_EMPTY || cvt==CV_NULL || cvt==CV_MISSING)
+ {
+ var->SetType(cvt);
+ }
+ else if (cvt==CV_ENUM)
+ {
+ var->SetDataAsInt64(*((INT32 *)UnboxData));
+ var->SetObjRef(typeHandle.GetManagedClassObject());
+ var->SetType(GetEnumFlags(typeHandle));
+ }
+ else
+ {
+ // Decimals and other boxed value classes get handled here.
+ var->SetObjRef(val);
+ }
+ }
+
+ GCPROTECT_END();
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+
+FCIMPL1(Object*, COMVariant::BoxEnum, VariantData* var)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(var));
+ PRECONDITION(var->GetObjRef() != NULL);
+ }
+ CONTRACTL_END;
+
+ OBJECTREF retO = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(retO);
+
+#ifdef _DEBUG
+ CVTypes vType = (CVTypes) var->GetType();
+#endif
+
+ _ASSERTE(vType == CV_ENUM);
+
+ MethodTable* mt = ((REFLECTCLASSBASEREF) var->GetObjRef())->GetType().GetMethodTable();
+ _ASSERTE(mt);
+
+ retO = mt->Box(var->GetData());
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(retO);
+}
+FCIMPLEND
+
+
+/*===============================GetTypeFromClass===============================
+**Action: Takes an MethodTable * and returns the associated CVType.
+**Arguments: MethodTable * -- a pointer to the class for which we want the CVType.
+**Returns: The CVType associated with the MethodTable or CV_OBJECT if this can't be
+** determined.
+**Exceptions: None
+==============================================================================*/
+
+CVTypes COMVariant::GetCVTypeFromClass(TypeHandle th)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (th.IsNull())
+ return CV_EMPTY;
+
+ //We'll start looking from Variant. Empty and Void are handled below.
+ for (int i=CV_EMPTY; i<CV_LAST; i++)
+ {
+ if (th == GetTypeHandleForCVType((CVTypes)i))
+ return (CVTypes)i;
+ }
+
+ if (th.IsEnum())
+ return CV_ENUM;
+
+ return CV_OBJECT;
+}
+
+
+int COMVariant::GetEnumFlags(TypeHandle th)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(!th.IsNull());
+ PRECONDITION(th.IsEnum());
+ }
+ CONTRACTL_END;
+
+ // <TODO> check this approximation - we may be losing exact type information </TODO>
+ ApproxFieldDescIterator fdIterator(th.GetMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS);
+ FieldDesc* p = fdIterator.Next();
+ if (NULL == p)
+ {
+ _ASSERTE(!"NULL FieldDesc returned");
+ return 0;
+ }
+
+#ifdef _DEBUG
+ WORD fldCnt = th.GetMethodTable()->GetNumInstanceFields();
+#endif
+
+ _ASSERTE(fldCnt == 1);
+
+ CorElementType cet = p[0].GetFieldType();
+ switch (cet)
+ {
+ case ELEMENT_TYPE_I1:
+ return (CV_ENUM | EnumI1);
+
+ case ELEMENT_TYPE_U1:
+ return (CV_ENUM | EnumU1);
+
+ case ELEMENT_TYPE_I2:
+ return (CV_ENUM | EnumI2);
+
+ case ELEMENT_TYPE_U2:
+ return (CV_ENUM | EnumU2);
+
+ IN_WIN32(case ELEMENT_TYPE_I:)
+ case ELEMENT_TYPE_I4:
+ return (CV_ENUM | EnumI4);
+
+ IN_WIN32(case ELEMENT_TYPE_U:)
+ case ELEMENT_TYPE_U4:
+ return (CV_ENUM | EnumU4);
+
+ IN_WIN64(case ELEMENT_TYPE_I:)
+ case ELEMENT_TYPE_I8:
+ return (CV_ENUM | EnumI8);
+
+ IN_WIN64(case ELEMENT_TYPE_U:)
+ case ELEMENT_TYPE_U8:
+ return (CV_ENUM | EnumU8);
+
+ default:
+ _ASSERTE(!"UNknown Type");
+ return 0;
+ }
+}
+
+#endif // FEATURE_COMINTEROP
diff --git a/src/classlibnative/bcltype/variant.h b/src/classlibnative/bcltype/variant.h
new file mode 100644
index 0000000000..0089693e23
--- /dev/null
+++ b/src/classlibnative/bcltype/variant.h
@@ -0,0 +1,55 @@
+// 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.
+//
+// File: Variant.h
+//
+
+//
+// Purpose: Headers for the Variant class.
+//
+
+//
+
+#ifndef _VARIANT_H_
+#define _VARIANT_H_
+
+#ifndef FEATURE_COMINTEROP
+#error FEATURE_COMINTEROP is required for this file
+#endif // FEATURE_COMINTEROP
+
+#include <cor.h>
+#include "fcall.h"
+#include "olevariant.h"
+
+class COMVariant
+{
+ friend class OleVariant;
+
+public:
+ //
+ // Helper Routines
+ //
+
+ //
+ // Initialization Methods
+
+ static FCDECL2_IV(void, SetFieldsR4, VariantData* vThisRef, float val);
+ static FCDECL2_IV(void, SetFieldsR8, VariantData* vThisRef, double val);
+ static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal);
+ static FCDECL1(float, GetR4FromVar, VariantData* var);
+ static FCDECL1(double, GetR8FromVar, VariantData* var);
+
+ static FCDECL0(void, InitVariant);
+
+ static FCDECL1(Object*, BoxEnum, VariantData* var);
+
+private:
+ // GetCVTypeFromClass
+ // This method will return the CVTypes from the Variant instance
+ static CVTypes GetCVTypeFromClass(TypeHandle th);
+ static int GetEnumFlags(TypeHandle th);
+};
+
+#endif // _VARIANT_H_
+
diff --git a/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp b/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp
new file mode 100644
index 0000000000..c99730e3aa
--- /dev/null
+++ b/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp
@@ -0,0 +1,44 @@
+// 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.
+
+#ifdef FEATURE_COMINTEROP
+
+#include "common.h"
+#include "ComCallableWrapper.h"
+#include "WindowsRuntimeBufferHelper.h"
+
+void QCALLTYPE WindowsRuntimeBufferHelper::StoreOverlappedPtrInCCW(QCall::ObjectHandleOnStack winRtBuffer, LPOVERLAPPED lpOverlapped) {
+
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ GCX_COOP();
+ OBJECTREF buffer = ObjectToOBJECTREF(*winRtBuffer.m_ppObject);
+
+ ComCallWrapper *ccw = ComCallWrapper::GetWrapperForObject(buffer);
+ SimpleComCallWrapper *simpleCCW = ccw->GetSimpleWrapper();
+
+ simpleCCW->StoreOverlappedPointer(lpOverlapped);
+
+ END_QCALL;
+}
+
+
+void WindowsRuntimeBufferHelper::ReleaseOverlapped(LPOVERLAPPED lpOverlapped) {
+
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+ OverlappedDataObject::GetOverlapped(lpOverlapped)->FreeAsyncPinHandles();
+}
+
+#endif // ifdef FEATURE_COMINTEROP
diff --git a/src/classlibnative/bcltype/windowsruntimebufferhelper.h b/src/classlibnative/bcltype/windowsruntimebufferhelper.h
new file mode 100644
index 0000000000..42596515a0
--- /dev/null
+++ b/src/classlibnative/bcltype/windowsruntimebufferhelper.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef _WINDOWSRUNTIMEBUFFERHELPER_H_
+#define _WINDOWSRUNTIMEBUFFERHELPER_H_
+
+#ifdef FEATURE_COMINTEROP
+
+#include "nativeoverlapped.h"
+#include "qcall.h"
+
+class WindowsRuntimeBufferHelper {
+
+private:
+
+
+public:
+
+ static void QCALLTYPE StoreOverlappedPtrInCCW(QCall::ObjectHandleOnStack winRtBuffer, LPOVERLAPPED lpOverlapped);
+ static void ReleaseOverlapped(LPOVERLAPPED lpOverlapped);
+
+};
+
+#endif // ifdef FEATURE_COMINTEROP
+
+#endif // _WINDOWSRUNTIMEBUFFERHELPER_H_
diff --git a/src/classlibnative/float/.gitmirror b/src/classlibnative/float/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/classlibnative/float/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/classlibnative/float/CMakeLists.txt b/src/classlibnative/float/CMakeLists.txt
new file mode 100644
index 0000000000..7cb7dedff3
--- /dev/null
+++ b/src/classlibnative/float/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+include_directories("../inc")
+
+set(FLOAT_SOURCES
+ floatdouble.cpp
+ floatsingle.cpp
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_library_clr(comfloat_wks
+ STATIC
+ ${FLOAT_SOURCES}
+)
diff --git a/src/classlibnative/float/Float.nativeproj b/src/classlibnative/float/Float.nativeproj
new file mode 100644
index 0000000000..ef7f818e0e
--- /dev/null
+++ b/src/classlibnative/float/Float.nativeproj
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(ClrBase)\src\debug\SetDebugTargetLocal.props" />
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <ClOptimization Condition="'$(DebugBuild)' == 'false'">Full</ClOptimization>
+ <UserIncludes>..\inc;$(UserIncludes);$(Clrbase)\src\vm;$(Clrbase)\src\vm\$(TargetCpu);$(VCToolsIncPath);$(Clrbase)\src\strongname\inc</UserIncludes>
+ <OutputName>comfloat_wks</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines>
+ <ClWarningLevel>4</ClWarningLevel>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="floatdouble.cpp" />
+ <CppCompile Include="floatsingle.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/classlibnative/float/floatdouble.cpp b/src/classlibnative/float/floatdouble.cpp
new file mode 100644
index 0000000000..d9603c0636
--- /dev/null
+++ b/src/classlibnative/float/floatdouble.cpp
@@ -0,0 +1,280 @@
+// 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.
+//
+// File: FloatDouble.cpp
+//
+
+#include <common.h>
+
+#include "floatdouble.h"
+
+#define IS_DBL_INFINITY(x) (((*((INT64*)((void*)&x))) & I64(0x7FFFFFFFFFFFFFFF)) == I64(0x7FF0000000000000))
+
+// The default compilation mode is /fp:precise, which disables floating-point intrinsics. This
+// default compilation mode has previously caused performance regressions in floating-point code.
+// We enable /fp:fast semantics for the majority of the math functions, as it will speed up performance
+// and is really unlikely to cause any other code regressions.
+
+// Sin, Cos, and Tan on AMD64 Windows were previously implemented in vm\amd64\JitHelpers_Fast.asm
+// by calling x87 floating point code (fsin, fcos, fptan) because the CRT helpers were too slow. This
+// is no longer the case and the CRT call is used on all platforms.
+
+// Log, Log10 and Exp were previously slower with /fp:fast on SSE2 enabled hardware (see #500373).
+// This is no longer the case and they now consume use the /fp:fast versions.
+
+// Exp(+/-INFINITY) did not previously return the expected results of +0.0 (for -INFINITY)
+// and +INFINITY (for +INFINITY) so these cases were handled specially. As this is no longer
+// the case and the expected results are now returned, the special handling has been removed.
+
+// Previously there was more special handling for the x86 Windows version of Pow.
+// This additional handling was unnecessary and has since been removed.
+
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+///
+/// beginning of /fp:fast scope
+///
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER
+#pragma float_control(precise, off)
+#endif
+
+/*=====================================Abs======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Abs, double x)
+ FCALL_CONTRACT;
+
+ return (double)fabs(x);
+FCIMPLEND
+
+/*=====================================Acos=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Acos, double x)
+ FCALL_CONTRACT;
+
+ return (double)acos(x);
+FCIMPLEND
+
+/*=====================================Asin=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Asin, double x)
+ FCALL_CONTRACT;
+
+ return (double)asin(x);
+FCIMPLEND
+
+/*=====================================Atan=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Atan, double x)
+ FCALL_CONTRACT;
+
+ return (double)atan(x);
+FCIMPLEND
+
+/*=====================================Atan2====================================
+**
+==============================================================================*/
+FCIMPL2_VV(double, COMDouble::Atan2, double y, double x)
+ FCALL_CONTRACT;
+
+ // atan2(+/-INFINITY, +/-INFINITY) produces +/-0.78539816339744828 (x is +INFINITY) and
+ // +/-2.3561944901923448 (x is -INFINITY) instead of the expected value of NaN. We handle
+ // that case here ourselves.
+ if (IS_DBL_INFINITY(y) && IS_DBL_INFINITY(x)) {
+ return (double)(y / x);
+ }
+
+ return (double)atan2(y, x);
+FCIMPLEND
+
+/*====================================Ceil======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Ceil, double x)
+ FCALL_CONTRACT;
+
+ return (double)ceil(x);
+FCIMPLEND
+
+/*=====================================Cos======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Cos, double x)
+ FCALL_CONTRACT;
+
+ return (double)cos(x);
+FCIMPLEND
+
+/*=====================================Cosh=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Cosh, double x)
+ FCALL_CONTRACT;
+
+ return (double)cosh(x);
+FCIMPLEND
+
+/*=====================================Exp======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Exp, double x)
+ FCALL_CONTRACT;
+
+ return (double)exp(x);
+FCIMPLEND
+
+/*====================================Floor=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Floor, double x)
+ FCALL_CONTRACT;
+
+ return (double)floor(x);
+FCIMPLEND
+
+/*=====================================Log======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Log, double x)
+ FCALL_CONTRACT;
+
+ return (double)log(x);
+FCIMPLEND
+
+/*====================================Log10=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Log10, double x)
+ FCALL_CONTRACT;
+
+ return (double)log10(x);
+FCIMPLEND
+
+/*=====================================ModF=====================================
+**
+==============================================================================*/
+FCIMPL1(double, COMDouble::ModF, double* iptr)
+ FCALL_CONTRACT;
+
+ return (double)modf(*iptr, iptr);
+FCIMPLEND
+
+/*=====================================Pow======================================
+**
+==============================================================================*/
+FCIMPL2_VV(double, COMDouble::Pow, double x, double y)
+ FCALL_CONTRACT;
+
+ // The CRT version of pow preserves the NaN payload of x over the NaN payload of y.
+
+ if(_isnan(y)) {
+ return y; // IEEE 754-2008: NaN payload must be preserved
+ }
+
+ if(_isnan(x)) {
+ return x; // IEEE 754-2008: NaN payload must be preserved
+ }
+
+ // The CRT version of pow does not return NaN for pow(-1.0, +/-INFINITY) and
+ // instead returns +1.0.
+
+ if(IS_DBL_INFINITY(y) && (x == -1.0)) {
+ INT64 result = CLR_NAN_64;
+ return (*((double*)((INT64*)&result)));
+ }
+
+ return (double)pow(x, y);
+FCIMPLEND
+
+/*====================================Round=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Round, double x)
+ FCALL_CONTRACT;
+
+ // If the number has no fractional part do nothing
+ // This shortcut is necessary to workaround precision loss in borderline cases on some platforms
+ if (x == (double)((INT64)x)) {
+ return x;
+ }
+
+ // We had a number that was equally close to 2 integers.
+ // We need to return the even one.
+
+ double tempVal = (x + 0.5);
+ double flrTempVal = floor(tempVal);
+
+ if ((flrTempVal == tempVal) && (fmod(tempVal, 2.0) != 0)) {
+ flrTempVal -= 1.0;
+ }
+
+ return _copysign(flrTempVal, x);
+FCIMPLEND
+
+/*=====================================Sin======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Sin, double x)
+ FCALL_CONTRACT;
+
+ return (double)sin(x);
+FCIMPLEND
+
+/*=====================================Sinh=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Sinh, double x)
+ FCALL_CONTRACT;
+
+ return (double)sinh(x);
+FCIMPLEND
+
+/*=====================================Sqrt=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Sqrt, double x)
+ FCALL_CONTRACT;
+
+ return (double)sqrt(x);
+FCIMPLEND
+
+/*=====================================Tan======================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Tan, double x)
+ FCALL_CONTRACT;
+
+ return (double)tan(x);
+FCIMPLEND
+
+/*=====================================Tanh=====================================
+**
+==============================================================================*/
+FCIMPL1_V(double, COMDouble::Tanh, double x)
+ FCALL_CONTRACT;
+
+ return (double)tanh(x);
+FCIMPLEND
+
+#ifdef _MSC_VER
+#pragma float_control(precise, on )
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+///
+/// End of /fp:fast scope
+///
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/classlibnative/float/floatsingle.cpp b/src/classlibnative/float/floatsingle.cpp
new file mode 100644
index 0000000000..dd1bb43316
--- /dev/null
+++ b/src/classlibnative/float/floatsingle.cpp
@@ -0,0 +1,54 @@
+// 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.
+//
+// File: FloatSingle.cpp
+//
+
+#include <common.h>
+
+#include "floatsingle.h"
+
+#define IS_FLT_INFINITY(x) (((*((INT32*)((void*)&x))) & 0x7FFFFFFF) == 0x7F800000)
+
+// The default compilation mode is /fp:precise, which disables floating-point intrinsics. This
+// default compilation mode has previously caused performance regressions in floating-point code.
+// We enable /fp:fast semantics for the majority of the math functions, as it will speed up performance
+// and is really unlikely to cause any other code regressions.
+
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+///
+/// beginning of /fp:fast scope
+///
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER
+#pragma float_control(precise, off)
+#endif
+
+/*=====================================Abs=====================================
+**
+==============================================================================*/
+FCIMPL1(float, COMSingle::Abs, float x)
+ FCALL_CONTRACT;
+
+ return (float)fabsf(x);
+FCIMPLEND
+
+#ifdef _MSC_VER
+#pragma float_control(precise, on )
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+///
+/// End of /fp:fast scope
+///
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/classlibnative/inc/.gitmirror b/src/classlibnative/inc/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/classlibnative/inc/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/classlibnative/inc/calendardata.h b/src/classlibnative/inc/calendardata.h
new file mode 100644
index 0000000000..f30660ee99
--- /dev/null
+++ b/src/classlibnative/inc/calendardata.h
@@ -0,0 +1,165 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: CalendarData
+//
+
+//
+// Purpose: This module implements the methods of the CalendarData
+// class. These methods are the helper functions for the
+// Locale class.
+//
+// Date: July 4, 2007
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef _CALENDARDATA_H
+#define _CALENDARDATA_H
+
+//
+// Data store for the calendar data.
+//
+class CalendarData : Object
+{
+
+ //
+ // WARNING: These properties should stay in-sync with CalendarData.cs
+ //
+private:
+ // Identity
+ STRINGREF sNativeName ; // Calendar Name for the locale (fallback for calendar only records)
+
+ // Formats
+ PTRARRAYREF saShortDates ; // Short Data format, default first
+ PTRARRAYREF saYearMonths ; // Year/Month Data format, default first
+ PTRARRAYREF saLongDates ; // Long Data format, default first
+ STRINGREF sMonthDay ; // Month/Day format
+
+ // Calendar Parts Names
+ PTRARRAYREF saEraNames ; // Names of Eras
+ PTRARRAYREF saAbbrevEraNames ; // Abbreviated Era Names
+ PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English
+ PTRARRAYREF saDayNames ; // Day Names, null to use locale data, starts on Sunday
+ PTRARRAYREF saAbbrevDayNames ; // Abbrev Day Names, null to use locale data, starts on Sunday
+ PTRARRAYREF saSuperShortDayNames ; // Super short Day of week names
+ PTRARRAYREF saMonthNames ; // Month Names (13)
+ PTRARRAYREF saAbbrevMonthNames ; // Abbrev Month Names (13)
+ PTRARRAYREF saMonthGenitiveNames ; // Genitive Month Names (13)
+ PTRARRAYREF saAbbrevMonthGenitiveNames; // Genitive Abbrev Month Names (13)
+ PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year.
+
+ // Year, digits have to be at end to make marshaller happy?
+ INT32 iTwoDigitYearMax ; // Max 2 digit year (for Y2K bug data entry)
+ INT32 iCurrentEra ; // our current era #
+
+ // Use overrides?
+ CLR_BOOL bUseUserOverrides ; // True if we want user overrides.
+
+ //
+ // Helpers
+ //
+ static BOOL CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef);
+ static BOOL CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32);
+ static BOOL GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings);
+ static BOOL GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings);
+// TODO: NLS Arrowhead -Windows 7 If the OS had data this could use it, but Windows doesn't expose data for eras in enough detail
+// static BOOL GetCalendarEraInfo(LPCWSTR localeName, int calendar, PTRARRAYREF* pOutputEras);
+ static BOOL CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType,
+ __in int lcType, __inout PTRARRAYREF* pOutputStrings);
+
+ static void CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer);
+public:
+ //
+ // ecall function for methods in CalendarData
+ //
+ static FCDECL1(INT32, nativeGetTwoDigitYearMax, INT32 nValue);
+ static FCDECL3(FC_BOOL_RET, nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar);
+ static FCDECL3(INT32, nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL bUseOverrides, I4Array* calendars);
+ static FCDECL3(Object*, nativeEnumTimeFormats, StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride);
+};
+
+typedef CalendarData* CALENDARDATAREF;
+
+#ifndef LOCALE_RETURN_GENITIVE_NAMES
+#define LOCALE_RETURN_GENITIVE_NAMES 0x10000000 //Flag to return the Genitive forms of month names
+#endif
+
+#ifndef CAL_RETURN_GENITIVE_NAMES
+#define CAL_RETURN_GENITIVE_NAMES LOCALE_RETURN_GENITIVE_NAMES // return genitive forms of month names
+#endif
+
+#ifndef CAL_SERASTRING
+#define CAL_SERASTRING 0x00000004 // era name for IYearOffsetRanges, eg A.D.
+#endif
+
+#ifndef CAL_SMONTHDAY
+#define CAL_SMONTHDAY 0x00000038 // Month/day pattern (reserve for potential inclusion in a future version)
+#define CAL_SABBREVERASTRING 0x00000039 // Abbreviated era string (eg: AD)
+#endif
+
+#define RESERVED_CAL_JULIAN 13 // Julian calendar (data looks like GREGORIAN_US)
+#define RESERVED_CAL_JAPANESELUNISOLAR 14 // Japaenese Lunisolar calendar (data looks like CAL_JAPANESE)
+#define RESERVED_CAL_CHINESELUNISOLAR 15 // Algorithmic
+#define RESERVED_CAL_SAKA 16 // reserved to match Office but not implemented in our code
+#define RESERVED_CAL_LUNAR_ETO_CHN 17 // reserved to match Office but not implemented in our code
+#define RESERVED_CAL_LUNAR_ETO_KOR 18 // reserved to match Office but not implemented in our code
+#define RESERVED_CAL_LUNAR_ETO_ROKUYOU 19 // reserved to match Office but not implemented in our code
+#define RESERVED_CAL_KOREANLUNISOLAR 20 // Algorithmic
+#define RESERVED_CAL_TAIWANLUNISOLAR 21 // Algorithmic
+#define RESERVED_CAL_PERSIAN 22 // Algorithmic
+
+// These are vista properties
+#ifndef CAL_UMALQURA
+#define CAL_UMALQURA 23
+#endif
+
+#ifndef CAL_SSHORTESTDAYNAME1
+#define CAL_SSHORTESTDAYNAME1 0x00000031
+#define CAL_SSHORTESTDAYNAME2 0x00000032
+#define CAL_SSHORTESTDAYNAME3 0x00000033
+#define CAL_SSHORTESTDAYNAME4 0x00000034
+#define CAL_SSHORTESTDAYNAME5 0x00000035
+#define CAL_SSHORTESTDAYNAME6 0x00000036
+#define CAL_SSHORTESTDAYNAME7 0x00000037
+#endif
+
+#ifndef CAL_ITWODIGITYEARMAX
+ #define CAL_ITWODIGITYEARMAX 0x00000030 // two digit year max
+#endif // CAL_ITWODIGITYEARMAX
+#ifndef CAL_RETURN_NUMBER
+ #define CAL_RETURN_NUMBER 0x20000000 // return number instead of string
+#endif // CAL_RETURN_NUMBER
+
+#ifndef LOCALE_SNAME
+#define LOCALE_SNAME 0x0000005c // locale name (ie: en-us)
+#define LOCALE_SDURATION 0x0000005d // time duration format
+#define LOCALE_SKEYBOARDSTOINSTALL 0x0000005e
+#define LOCALE_SSHORTESTDAYNAME1 0x00000060 // Shortest day name for Monday
+#define LOCALE_SSHORTESTDAYNAME2 0x00000061 // Shortest day name for Tuesday
+#define LOCALE_SSHORTESTDAYNAME3 0x00000062 // Shortest day name for Wednesday
+#define LOCALE_SSHORTESTDAYNAME4 0x00000063 // Shortest day name for Thursday
+#define LOCALE_SSHORTESTDAYNAME5 0x00000064 // Shortest day name for Friday
+#define LOCALE_SSHORTESTDAYNAME6 0x00000065 // Shortest day name for Saturday
+#define LOCALE_SSHORTESTDAYNAME7 0x00000066 // Shortest day name for Sunday
+#define LOCALE_SISO639LANGNAME2 0x00000067 // 3 character ISO abbreviated language name
+#define LOCALE_SISO3166CTRYNAME2 0x00000068 // 3 character ISO country name
+#define LOCALE_SNAN 0x00000069 // Not a Number
+#define LOCALE_SPOSINFINITY 0x0000006a // + Infinity
+#define LOCALE_SNEGINFINITY 0x0000006b // - Infinity
+#define LOCALE_SSCRIPTS 0x0000006c // Typical scripts in the locale
+#define LOCALE_SPARENT 0x0000006d // Fallback name for resources
+#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console
+#define LOCALE_SLANGDISPLAYNAME 0x0000006f // Lanugage Display Name for a language
+#endif // LOCALE_SNAME
+#ifndef LOCALE_SSHORTTIME
+#define LOCALE_SSHORTTIME 0x00000079 // short time format (ie: no seconds, just h:mm)
+#endif // LOCALE_SSHORTTIME
+
+#ifndef TIME_NOSECONDS
+#define TIME_NOSECONDS 0x00000002 // do not use seconds
+#endif
+
+#endif
+
diff --git a/src/classlibnative/inc/floatdouble.h b/src/classlibnative/inc/floatdouble.h
new file mode 100644
index 0000000000..16403c1f36
--- /dev/null
+++ b/src/classlibnative/inc/floatdouble.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef _FLOATDOUBLE_H_
+#define _FLOATDOUBLE_H_
+
+#include <object.h>
+#include <fcall.h>
+
+class COMDouble {
+public:
+ FCDECL1_V(static double, Abs, double x);
+ FCDECL1_V(static double, Acos, double x);
+ FCDECL1_V(static double, Asin, double x);
+ FCDECL1_V(static double, Atan, double x);
+ FCDECL2_VV(static double, Atan2, double y, double x);
+ FCDECL1_V(static double, Ceil, double x);
+ FCDECL1_V(static double, Cos, double x);
+ FCDECL1_V(static double, Cosh, double x);
+ FCDECL1_V(static double, Exp, double x);
+ FCDECL1_V(static double, Floor, double x);
+ FCDECL1_V(static double, Log, double x);
+ FCDECL1_V(static double, Log10, double x);
+ FCDECL1(static double, ModF, double* iptr);
+ FCDECL2_VV(static double, Pow, double x, double y);
+ FCDECL1_V(static double, Round, double x);
+ FCDECL1_V(static double, Sin, double x);
+ FCDECL1_V(static double, Sinh, double x);
+ FCDECL1_V(static double, Sqrt, double x);
+ FCDECL1_V(static double, Tan, double x);
+ FCDECL1_V(static double, Tanh, double x);
+};
+
+#endif // _FLOATDOUBLE_H_
diff --git a/src/classlibnative/inc/floatsingle.h b/src/classlibnative/inc/floatsingle.h
new file mode 100644
index 0000000000..6d123ec001
--- /dev/null
+++ b/src/classlibnative/inc/floatsingle.h
@@ -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.
+
+#ifndef _FLOATSINGLE_H_
+#define _FLOATSINGLE_H_
+
+#include <object.h>
+#include <fcall.h>
+
+class COMSingle {
+public:
+ FCDECL1(static float, Abs, float x);
+};
+
+#endif // _FLOATSINGLE_H_
diff --git a/src/classlibnative/inc/nls.h b/src/classlibnative/inc/nls.h
new file mode 100644
index 0000000000..872573982b
--- /dev/null
+++ b/src/classlibnative/inc/nls.h
@@ -0,0 +1,34 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////
+//
+// Module: NLS
+//
+
+//
+// Purpose: This module defines the common header information for
+// the Globalization classes.
+//
+// Date: August 12, 1998
+//
+////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef _NLS_H_
+#define _NLS_H_
+
+
+//
+// Constant Declarations.
+//
+
+#define HIGH_SURROGATE_START 0xd800
+#define HIGH_SURROGATE_END 0xdbff
+#define LOW_SURROGATE_START 0xdc00
+#define LOW_SURROGATE_END 0xdfff
+
+#define PRIVATE_USE_BEGIN 0xe000
+#define PRIVATE_USE_END 0xf8ff
+
+#endif // _NLS_H_
diff --git a/src/classlibnative/inc/nlsinfo.h b/src/classlibnative/inc/nlsinfo.h
new file mode 100644
index 0000000000..1c1ff01f8a
--- /dev/null
+++ b/src/classlibnative/inc/nlsinfo.h
@@ -0,0 +1,249 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: NLSInfo
+//
+
+//
+// Purpose: This module defines the methods of the COMNlsInfo
+// class. These methods are the helper functions for the
+// managed NLS+ classes.
+//
+// Date: August 12, 1998
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef _NLSINFO_H_
+#define _NLSINFO_H_
+
+#define DEFAULT_SORT_VERSION 0
+#define SORT_VERSION_WHIDBEY 0x00001000
+#define SORT_VERSION_V4 0x00060101
+
+//
+//This structure must map 1-for-1 with the InternalDataItem structure in
+//System.Globalization.EncodingTable.
+//
+struct EncodingDataItem {
+ const char * webName;
+ unsigned short codePage;
+ // free space here
+};
+
+//
+//This structure must map 1-for-1 with the InternalCodePageDataItem structure in
+//System.Globalization.EncodingTable.
+//
+struct CodePageDataItem {
+ unsigned short codePage;
+ unsigned short uiFamilyCodePage;
+ DWORD dwFlags; // only 4-bit used now
+ const char * names;
+};
+
+// Normalization
+typedef BOOL (*PFN_NORMALIZATION_IS_NORMALIZED_STRING)
+ ( int NormForm, LPCWSTR lpInString, int cchInString);
+
+typedef int (*PFN_NORMALIZATION_NORMALIZE_STRING)
+ ( int NormForm, LPCWSTR lpInString, int cchInString, LPWSTR lpOutString, int cchOutString);
+
+typedef BYTE* (*PFN_NORMALIZATION_INIT_NORMALIZATION)
+ ( int NormForm, BYTE* pTableData);
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Forward declarations
+//
+////////////////////////////////////////////////////////////////////////////
+
+class CharTypeTable;
+class CasingTable;
+class SortingTable;
+class NativeTextInfo;
+class CultureDataBaseObject;
+
+class COMNlsInfo {
+
+public:
+#ifdef FEATURE_SYNTHETIC_CULTURES
+ static INT32 WstrToInteger4(__in_z LPCWSTR wstrLocale, __in int Radix);
+#endif // FEATURE_SYNTHETIC_CULTURES
+
+ static INT32 GetCHTLanguage();
+ static INT32 CallGetSystemDefaultUILanguage();
+ static INT32 CallGetUserDefaultUILanguage();
+ static LANGID GetDownLevelSystemDefaultUILanguage();
+
+ //
+ // Native helper functions for methods in CultureInfo.
+ //
+ static BOOL QCALLTYPE InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName);
+ static BOOL QCALLTYPE InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage);
+ static BOOL QCALLTYPE InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage);
+
+// Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
+#ifdef FEATURE_CORECLR
+ static FCDECL0(Object*, nativeGetResourceFallbackArray);
+#endif
+
+ //
+ // Native helper functions for methods in DateTimeFormatInfo
+ //
+ static FCDECL1(FC_BOOL_RET, nativeSetThreadLocale, StringObject* localeNameUNSAFE);
+ static FCDECL2(Object*, nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType);
+ static FCDECL2(INT32, nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType);
+
+ //
+ // Native helper functions for CultureData
+ //
+ static FCDECL1(FC_BOOL_RET, nativeInitCultureData, CultureDataBaseObject *data);
+ static FCDECL3(FC_BOOL_RET, nativeGetNumberFormatInfoValues, StringObject* localeNameUNSAFE, NumberFormatInfo* nfi, CLR_BOOL useUserOverride);
+ static FCDECL1(Object*, LCIDToLocaleName, LCID lcid);
+ static FCDECL1(INT32, LocaleNameToLCID, StringObject* localeNameUNSAFE);
+
+ static INT32 QCALLTYPE InternalCompareString (INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string1, INT32 offset1, INT32 length1, LPCWSTR string2, INT32 offset2, INT32 length2, INT32 flags);
+ static INT32 QCALLTYPE InternalGetGlobalizedHashCode(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR pString, INT32 length, INT32 dwFlagsIn, BOOL bForceRandomizedHashing, INT64 additionalEntropy);
+
+ static BOOL QCALLTYPE InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR pString, INT32 length);
+ static INT_PTR QCALLTYPE InternalInitSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin);
+ static INT_PTR InitSortHandleHelper(LPCWSTR localeName, INT_PTR* handleOrigin);
+ static INT_PTR InternalInitOsSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin);
+#ifndef FEATURE_CORECLR
+ static INT_PTR InternalInitVersionedSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin);
+ static INT_PTR InternalInitVersionedSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin, DWORD sortVersion);
+ static DWORD QCALLTYPE InternalGetSortVersion();
+ static BOOL QCALLTYPE InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation);
+#endif
+
+
+#ifndef FEATURE_CORECLR
+ //
+ // Native helper function for methods in TimeZone
+ //
+ static FCDECL0(LONG, nativeGetTimeZoneMinuteOffset);
+ static FCDECL0(Object*, nativeGetStandardName);
+ static FCDECL0(Object*, nativeGetDaylightName);
+ static FCDECL1(Object*, nativeGetDaylightChanges, int year);
+#endif // FEATURE_CORECLR
+
+ //
+ // Native helper function for methods in EncodingTable
+ //
+ static FCDECL0(INT32, nativeGetNumEncodingItems);
+ static FCDECL0(EncodingDataItem *, nativeGetEncodingTableDataPointer);
+ static FCDECL0(CodePageDataItem *, nativeGetCodePageTableDataPointer);
+#if FEATURE_CODEPAGES_FILE
+ static FCDECL3(LPVOID, nativeCreateOpenFileMapping,
+ StringObject* inSectionNameUNSAFE, int inBytesToAllocate, HANDLE *mappedFile);
+#endif // FEATURE_CODEPAGES_FILE
+
+ //
+ // Native helper function for methods in CharacterInfo
+ //
+ static FCDECL0(void, AllocateCharTypeTable);
+
+ //
+ // Native helper function for methods in TextInfo
+ //
+ static FCDECL5(FC_CHAR_RET, InternalChangeCaseChar, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper);
+ static FCDECL5(Object*, InternalChangeCaseString, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pString, CLR_BOOL bIsToUpper);
+ static FCDECL6(INT32, InternalGetCaseInsHash, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID strA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy);
+ static INT32 QCALLTYPE InternalCompareStringOrdinalIgnoreCase(LPCWSTR string1, INT32 index1, LPCWSTR string2, INT32 index2, INT32 length1, INT32 length2);
+
+ static BOOL QCALLTYPE InternalTryFindStringOrdinalIgnoreCase(
+ __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH
+ __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
+ __in int cchSource, // number of characters lpStringSource after sourceIndex
+ __in int sourceIndex, // index from where the search will start in lpStringSource
+ __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
+ __in int cchValue,
+ __out int* foundIndex); // the index in lpStringSource where we found lpStringValue
+
+ //
+ // Native helper function for methods in Normalization
+ //
+ static FCDECL6(int, nativeNormalizationNormalizeString,
+ int NormForm, int& iError,
+ StringObject* inString, int inLength,
+ CHARArray* outChars, int outLength);
+ static FCDECL4(FC_BOOL_RET, nativeNormalizationIsNormalizedString,
+ int NormForm, int& iError,
+ StringObject* inString, int cwLength);
+
+ static void QCALLTYPE nativeNormalizationInitNormalization(int NormForm, BYTE* pTableData);
+
+ //
+ // QCalls prototype
+ //
+
+ static int QCALLTYPE nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray);
+
+ static int QCALLTYPE InternalFindNLSStringEx(
+ __in_opt INT_PTR handle, // optional sort handle
+ __in_opt INT_PTR handleOrigin, // optional pointer to the native function that created the sort handle
+ __in_z LPCWSTR lpLocaleName, // locale name
+ __in int dwFindNLSStringFlags, // search falg
+ __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
+ __in int cchSource, // number of characters lpStringSource after sourceIndex
+ __in int sourceIndex, // index from where the search will start in lpStringSource
+ __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
+ __in int cchValue); // length of the string we search for
+
+ static int QCALLTYPE InternalGetSortKey(
+ __in_opt INT_PTR handle, // PSORTHANDLE
+ __in_opt INT_PTR handleOrigin, // optional pointer to the native function that created the sort handle
+ __in_z LPCWSTR pLocaleName, // locale name
+ __in int flags, // flags
+ __in_ecount(cchSource) LPCWSTR pStringSource, // Source string
+ __in int cchSource, // number of characters in lpStringSource
+ __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count)
+ __in int cchTarget); // Character count for target buffer
+
+
+private:
+
+ //
+ // Internal helper functions.
+ //
+ static LPVOID internalEnumSystemLocales(DWORD dwFlags);
+ static INT32 CompareOrdinal(__in_ecount(Length1) WCHAR* strAChars, int Length1, __in_ecount(Length2) WCHAR* strBChars, int Length2 );
+ static INT32 FastIndexOfString(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength);
+ static INT32 FastIndexOfStringInsensitive(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength);
+ static INT32 FastLastIndexOfString(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength);
+ static INT32 FastLastIndexOfStringInsensitive(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength);
+
+ static BOOL GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF* pOutputStrAry, BOOL useUserOverride);
+ static BOOL CallGetLocaleInfoEx(LPCWSTR locale, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride);
+ static BOOL CallGetLocaleInfoEx(LPCWSTR locale, int lcType, INT32* pOutputInt32, BOOL useUserOverride);
+
+ static BOOL IsWindows7();
+
+ //
+ // Definitions.
+ //
+
+#ifndef FEATURE_COREFX_GLOBALIZATION
+ // Normalization
+ static HMODULE m_hNormalization;
+ static PFN_NORMALIZATION_IS_NORMALIZED_STRING m_pfnNormalizationIsNormalizedStringFunc;
+ static PFN_NORMALIZATION_NORMALIZE_STRING m_pfnNormalizationNormalizeStringFunc;
+ static PFN_NORMALIZATION_INIT_NORMALIZATION m_pfnNormalizationInitNormalizationFunc;
+#endif
+
+private:
+ //
+ // Internal encoding data tables.
+ //
+ const static int m_nEncodingDataTableItems;
+ const static EncodingDataItem EncodingDataTable[];
+
+ const static int m_nCodePageTableItems;
+ const static CodePageDataItem CodePageDataTable[];
+
+ static INT_PTR EnsureValidSortHandle(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName);
+};
+
+#endif // _NLSINFO_H_
diff --git a/src/classlibnative/inc/nlstable.h b/src/classlibnative/inc/nlstable.h
new file mode 100644
index 0000000000..b5f5a4bca6
--- /dev/null
+++ b/src/classlibnative/inc/nlstable.h
@@ -0,0 +1,121 @@
+// 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.
+
+// ==++==
+//
+
+//
+
+//
+// TODO: NLS Arrowhead - when we have win7 unicode support this can "go away"
+// ==--==
+#ifndef _NLSTABLE_H
+#define _NLSTABLE_H
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: NLSTable
+//
+// Purpose: The base class for NLS+ table. This class provides the utility
+// functions to open and map a view of NLS+ data files.
+//
+// Date: August 31, 1999
+//
+////////////////////////////////////////////////////////////////////////////
+
+typedef LPWORD P844_TABLE; // ptr to 8:4:4 table
+
+//
+// Macros For High and Low Nibbles of a BYTE.
+//
+#define LO_NIBBLE(b) ((BYTE)((BYTE)(b) & 0xF))
+#define HI_NIBBLE(b) ((BYTE)(((BYTE)(b) >> 4) & 0xF))
+
+//
+// Macros for Extracting the 8:4:4 Index Values.
+//
+#define GET8(w) (HIBYTE(w))
+#define GETHI4(w) (HI_NIBBLE(LOBYTE(w)))
+#define GETLO4(w) (LO_NIBBLE(LOBYTE(w)))
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Traverse844Byte
+//
+// Traverses the 8:4:4 translation table for the given wide character. It
+// returns the final value of the 8:4:4 table, which is a BYTE in length.
+//
+// NOTE: Offsets in table are in BYTES.
+//
+// Broken Down Version:
+// --------------------
+// Incr = pTable[GET8(wch)] / sizeof(WORD);
+// Incr = pTable[Incr + GETHI4(wch)];
+// Value = (BYTE *)pTable[Incr + GETLO4(wch)];
+//
+//
+////////////////////////////////////////////////////////////////////////////
+
+inline BYTE& Traverse844Byte(const WORD * pTable, WCHAR wch)
+{
+ return (
+ ((BYTE *)pTable)[
+ pTable[
+ (pTable[GET8(wch)] / sizeof(WORD)) + GETHI4(wch)
+ ] + GETLO4(wch)
+ ]
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Traverse844Word
+//
+// Traverses the 8:4:4 translation table for the given wide character. It
+// returns the final value of the 8:4:4 table, which is a WORD in length.
+//
+// Broken Down Version:
+// --------------------
+// Incr = pTable[GET8(wch)];
+// Incr = pTable[Incr + GETHI4(wch)];
+// Value = pTable[Incr + GETLO4(wch)];
+//
+//
+////////////////////////////////////////////////////////////////////////////
+
+inline WORD Traverse844Word(const WORD * pTable, WCHAR wch)
+{
+ return (pTable[pTable[pTable[GET8(wch)] + GETHI4(wch)] + GETLO4(wch)]);
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// GET_INCR_VALUE
+//
+// Gets the value of a given wide character from the given 8:4:4 table. It
+// then uses the value as an increment by adding it to the given wide
+// character code point.
+//
+// NOTE: Whenever there is no translation for the given code point, the
+// tables will return an increment value of 0. This way, the
+// wide character passed in is the same value that is returned.
+//
+// DEFINED AS A MACRO.
+//
+//
+////////////////////////////////////////////////////////////////////////////
+
+inline WCHAR GetIncrValue(const WORD * p844Tbl, WCHAR wch)
+{
+ return ((WCHAR)(wch + Traverse844Word(p844Tbl, wch)));
+}
+
+class NLSTable
+{
+ public:
+ static HANDLE CreateSharedMemoryMapping(const LPCWSTR pMappingName, const int iSize );
+ static PBYTE OpenOrCreateMemoryMapping(const LPCWSTR pMappingName, const int iSize , HANDLE *mappedFile);
+};
+
+#endif // _NLSTABLE_H
diff --git a/src/classlibnative/nls/.gitmirror b/src/classlibnative/nls/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/classlibnative/nls/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/classlibnative/nls/CMakeLists.txt b/src/classlibnative/nls/CMakeLists.txt
new file mode 100644
index 0000000000..d109ec2eb0
--- /dev/null
+++ b/src/classlibnative/nls/CMakeLists.txt
@@ -0,0 +1,8 @@
+set( COMMLS_WKS_SOURCES
+ calendardata.cpp
+ encodingdata.cpp
+ nlsinfo.cpp
+ nlstable.cpp
+)
+
+add_library_clr(comnls_wks ${COMMLS_WKS_SOURCES})
diff --git a/src/classlibnative/nls/NLS.nativeproj b/src/classlibnative/nls/NLS.nativeproj
new file mode 100644
index 0000000000..8a08bc89ed
--- /dev/null
+++ b/src/classlibnative/nls/NLS.nativeproj
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(ClrBase)\src\debug\SetDebugTargetLocal.props" />
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <!--Auto-converted: COMPILER_WARNINGS-->
+ <UserIncludes>$(UserIncludes);..\inc;$(Clrbase)\src\vm;$(Clrbase)\src\vm\$(TargetCpu);$(VCToolsIncPath);$(Clrbase)\src\strongname\inc;..\bcltype</UserIncludes>
+ <OutputName>comnls_wks</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <CDefines>$(CDefines);UNICODE;_UNICODE</CDefines>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="CalendarData.cpp" />
+ <CppCompile Include="EncodingData.cpp" />
+ <CppCompile Include="NLSInfo.cpp" />
+ <CppCompile Include="NLSTable.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/classlibnative/nls/calendardata.cpp b/src/classlibnative/nls/calendardata.cpp
new file mode 100644
index 0000000000..95d071cc93
--- /dev/null
+++ b/src/classlibnative/nls/calendardata.cpp
@@ -0,0 +1,985 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: CalendarData
+//
+
+//
+// Purpose: This module implements the methods of the CalendarData
+// class. These methods are the helper functions for the
+// Locale class.
+//
+// Date: July 4, 2007
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "vars.hpp"
+#include "interoputil.h"
+#include "corhost.h"
+
+#include <winnls.h>
+
+#include "calendardata.h"
+#include "nlsinfo.h"
+#include "newapis.h"
+
+////////////////////////////////////////////////////////////////////////
+//
+// Call the Win32 GetCalendarInfoEx() using the specified calendar and LCTYPE.
+// The return value can be INT32 or an allocated managed string object, depending on
+// which version's called.
+//
+// Parameters:
+// OUT pOutputInt32 The output int32 value.
+// OUT pOutputRef The output string value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ int result = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
+
+ // Just stick it right into the output int
+ _ASSERT((calType & CAL_RETURN_NUMBER) != 0);
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, NULL, 0, (LPDWORD)pOutputInt32);
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ if (result != 0)
+ {
+ _ASSERTE(pOutputStrRef != NULL);
+ *pOutputStrRef = StringObject::NewString(buffer, result - 1);
+ }
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Get the native day names
+//
+// NOTE: There's a disparity between .Net & windows day orders, the input day should
+// start with Sunday
+//
+// Parameters:
+// OUT pOutputStrings The output string[] value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
+
+ //
+ // We'll need a new array of 7 items
+ //
+ // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
+ // an OutOfMemoryException if there's not enough memory.
+ //
+ PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(7, g_pStringClass);
+
+ GCPROTECT_BEGIN(ResultArray);
+
+ // Get each one of them
+ for (int i = 0; i < 7; i++, calType++)
+ {
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ // Note that the returned string is null terminated, so even an empty string will be 1
+ if (result != 0)
+ {
+ // Make a string for this entry
+ STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
+ ResultArray->SetAt(i, (OBJECTREF)stringResult);
+ }
+
+ // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens
+ // This is because the framework starts on sunday and windows starts on monday when counting days
+ if (i == 0) calType -= 7;
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = ResultArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Get the native month names
+//
+// Parameters:
+// OUT pOutputStrings The output string[] value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL CalendarData::GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // The maximum size for values returned from GetLocaleInfo is 80 characters.
+ WCHAR buffer[80];
+ int result = 0;
+
+ _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
+
+ //
+ // We'll need a new array of 13 items
+ //
+ // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
+ // an OutOfMemoryException if there's not enough memory.
+ //
+ PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(13, g_pStringClass);
+
+ GCPROTECT_BEGIN(ResultArray);
+
+ // Get each one of them
+ for (int i = 0; i < 13; i++, calType++)
+ {
+ result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
+
+ // If we still have failure, then mark as empty string
+ if (result == 0)
+ {
+ buffer[0] = W('0');
+ result = 1;
+
+
+ }
+
+ // Note that the returned string is null terminated, so even an empty string will be 1
+ // Make a string for this entry
+ STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
+ ResultArray->SetAt(i, (OBJECTREF)stringResult);
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = ResultArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return (result != 0);
+}
+
+//
+// struct to help our calendar data enumaration callback
+//
+struct enumData
+{
+ int count; // # of strings found so far
+ LPWSTR userOverride; // pointer to user override string if used
+ LPWSTR stringsBuffer; // pointer to a buffer to use for the strings.
+ LPWSTR endOfBuffer; // pointer to the end of the stringsBuffer ( must be < this to write)
+};
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumCalendarInfoCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpCalendarInfoString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumData* pData = (enumData*)lParam;
+
+ // If we had a user override, check to make sure this differs
+ if (pData->userOverride == NULL ||
+ wcscmp(pData->userOverride, lpCalendarInfoString) != 0)
+ {
+ // They're different, add it to our buffer
+ LPWSTR pStart = pData->stringsBuffer;
+ LPCWSTR pEnd = pData->endOfBuffer;
+ while (pStart < pEnd && *lpCalendarInfoString != 0)
+ {
+ *(pStart++) = *(lpCalendarInfoString++);
+ }
+
+ // Add a \0
+ if (pStart < pEnd)
+ {
+ *(pStart++) = 0;
+
+ // Did it finish?
+ if (pStart <= pEnd)
+ {
+ // It finished, use it
+ pData->count++;
+ pData->stringsBuffer = pStart;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//
+// CallEnumCalendarInfo
+//
+// Get the list of whichever calendar property from the OS. If user override is passed in, then check GetLocaleInfo as well
+// to see if a user override is set.
+//
+// We build a list of strings, first calling getlocaleinfo if necessary, and then the enums. The strings are null terminated,
+// with a double null ending the list. Once we have the list we can allocate our COMStrings and arrays from the count.
+//
+// We need a helper structure to pass as an lParam
+//
+BOOL CalendarData::CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType,
+ __in int lcType, __inout PTRARRAYREF* pOutputStrings)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ INJECT_FAULT(COMPlusThrowOM());
+ DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ BOOL result = TRUE;
+
+ // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
+ WCHAR stringBuffer[512];
+
+ struct enumData data;
+ data.count = 0;
+ data.userOverride = NULL;
+ data.stringsBuffer = stringBuffer;
+ data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
+
+ // First call GetLocaleInfo if necessary
+ if ((lcType && ((lcType & LOCALE_NOUSEROVERRIDE) == 0)) &&
+ // Get user locale, see if it matches localeName.
+ // Note that they should match exactly, including letter case
+ NewApis::GetUserDefaultLocaleName(stringBuffer, 512) && wcscmp(localeName, stringBuffer) == 0)
+ {
+ // They want user overrides, see if the user calendar matches the input calendar
+ CALID userCalendar = 0;
+ NewApis::GetLocaleInfoEx(localeName, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, (LPWSTR)&userCalendar,
+ sizeof(userCalendar) / sizeof(WCHAR) );
+
+ // If the calendars were the same, see if the locales were the same
+ if ((int)userCalendar == calendar) // todo: cast to compile on MAC
+ {
+ // They matched, get the user override since locale & calendar match
+ int i = NewApis::GetLocaleInfoEx(localeName, lcType, stringBuffer, 512);
+
+ // if it succeeded, advance the pointer and remember the override for the later callers
+ if (i > 0)
+ {
+ // Remember this was the override (so we can look for duplicates later in the enum function)
+ data.userOverride = data.stringsBuffer;
+
+ // Advance to the next free spot (i includes counting the \0)
+ data.stringsBuffer += i;
+
+ // And our count...
+ data.count++;
+ }
+ }
+ }
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, calendar, calType, (LPARAM)&data);
+
+ // Now we have a list of data, fail if we didn't find anything.
+ if (data.count == 0) return FALSE;
+
+ // Now we need to allocate our stringarray and populate it
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return (result); )
+
+ // Get our array object (will throw, don't have to check it)
+ PTRARRAYREF dataArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
+
+ GCPROTECT_BEGIN(dataArray);
+ LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
+ for(DWORD i = 0; i < (DWORD)data.count; i++)
+ {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
+
+ if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING)
+ {
+ // Eras are enumerated backwards. (oldest era name first, but
+ // Japanese calendar has newest era first in array, and is only
+ // calendar with multiple eras)
+ dataArray->SetAt((DWORD)data.count - i - 1, o);
+ }
+ else
+ {
+ dataArray->SetAt(i, o);
+ }
+
+ buffer += (lstrlenW(buffer) + 1);
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrings != NULL);
+ *pOutputStrings = dataArray;
+
+ END_SO_INTOLERANT_CODE
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// For calendars like Gregorain US/Taiwan/UmAlQura, they are not available
+// in all OS or all localized versions of OS.
+// If OS does not support these calendars, we will fallback by using the
+// appropriate fallback calendar and locale combination to retrieve data from OS.
+//
+// Parameters:
+// __deref_inout pCalendarInt:
+// Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed.
+// __in_out pLocaleNameStackBuffer
+// Pointer to the StackSString object which holds the locale name to be checked.
+// This will be updated to new fallback locale name if needed.
+//
+////////////////////////////////////////////////////////////////////////
+
+void CalendarData::CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer)
+{
+ // Gregorian-US isn't always available in the OS, however it is the same for all locales
+ switch (*pCalendarInt)
+ {
+ case CAL_GREGORIAN_US:
+ // See if this works
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS
+ pLocaleNameStackBuffer->Set(W("fa-IR"), 5);
+ }
+ // See if that works
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // Failed again, just use en-US with the gregorian calendar
+ pLocaleNameStackBuffer->Set(W("en-US"), 5);
+ *pCalendarInt = CAL_GREGORIAN;
+ }
+ break;
+ case CAL_TAIWAN:
+ // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons.
+ // It is only available in zh-TW localized versions of Windows.
+ // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar.
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ *pCalendarInt = CAL_GREGORIAN;
+ }
+ break;
+ case CAL_UMALQURA:
+ // UmAlQura is only available in Vista and above, so we will need to fallback to Hijri if it is not available in the OS.
+ if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
+ {
+ // There are no differences in DATA between UmAlQura and Hijri, and
+ // UmAlQura isn't available before Vista, so just use Hijri..
+ *pCalendarInt = CAL_HIJRI;
+ }
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Implementation for CalendarInfo.nativeGetCalendarData
+//
+// Retrieve calendar properties from the native side
+//
+// Parameters:
+// pCalendarData: This is passed from a managed structure CalendarData.cs
+// pLocaleNameUNSAFE: Locale name associated with the locale for this calendar
+// calendar: Calendar ID
+//
+// NOTE: Calendars depend on the locale name that creates it. Only a few
+// properties are available without locales using CalendarData.GetCalendar(int)
+//
+////////////////////////////////////////////////////////////////////////
+
+FCIMPL3(FC_BOOL_RET, CalendarData::nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+
+ // The maximum allowed string length in GetLocaleInfo is 80 WCHARs.
+ BOOL ret = TRUE;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ CALENDARDATAREF calendarData;
+ } gc;
+
+ // Dereference our gc objects
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.calendarData = (CALENDARDATAREF)calendarDataUNSAFE;
+
+ // Need to set up the frame since we will be allocating managed strings.
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // create a local copy of the string in order to pass it to helper methods that trigger GCs like GetCalendarDayInfo and GetCalendarMonthInfo
+ StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
+
+ // Conveniently this is the same as LOCALE_NOUSEROVERRIDE, so we can use this for both
+ int useOverrides = (gc.calendarData->bUseUserOverrides) ? 0 : CAL_NOUSEROVERRIDE;
+
+ // Helper string
+ STRINGREF stringResult = NULL;
+
+ //
+ // Windows doesn't support some calendars right now, so remap those.
+ //
+ if (calendar >= 13 && calendar < 23)
+ {
+ switch (calendar)
+ {
+ case RESERVED_CAL_PERSIAN: // don't change if we have Persian calendar
+ break;
+
+ case RESERVED_CAL_JAPANESELUNISOLAR: // Data looks like Japanese
+ calendar=CAL_JAPAN;
+ break;
+ case RESERVED_CAL_JULIAN: // Data looks like gregorian US
+ case RESERVED_CAL_CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent
+ case RESERVED_CAL_SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent
+ case RESERVED_CAL_KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent
+ case RESERVED_CAL_TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent
+ default:
+ calendar = CAL_GREGORIAN_US;
+ break;
+ }
+ }
+
+ //
+ // Speical handling for some special calendar due to OS limitation.
+ // This includes calendar like Taiwan calendar, UmAlQura calendar, etc.
+ //
+ CheckSpecialCalendar(&calendar, &localeNameStackBuffer);
+
+
+ // Numbers
+ ret &= CallGetCalendarInfoEx(localeNameStackBuffer, calendar,
+ CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER | useOverrides,
+ &(gc.calendarData->iTwoDigitYearMax));
+
+ if (ret == FALSE) // failed call
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_ITWODIGITYEARMAX");
+ }
+
+ _ASSERTE(ret == TRUE);
+
+ // Strings
+ if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SCALNAME , &stringResult))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->sNativeName), stringResult, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SCALNAME");
+ ret = FALSE;
+ }
+ if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SMONTHDAY | useOverrides, &stringResult))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->sMonthDay), stringResult, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read RESERVED_CAL_SMONTHDAY");
+ ret = FALSE;
+ }
+
+ // String Arrays
+ // Formats
+ PTRARRAYREF array = NULL;
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saShortDates), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SSHORTDATE");
+ ret = FALSE;
+ }
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saLongDates), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SLONGDATE");
+ ret = FALSE;
+ }
+
+ // Get the YearMonth pattern.
+ // Before Windows Vista, NLS would not write the Year/Month pattern into the reg key.
+ // This causes GetLocaleInfo() to retrieve the Gregorian localized calendar pattern even when the calendar is not Gregorian localized.
+ // So we will call GetLocaleInfo() only when the reg key for sYearMonth is there.
+ //
+ // If the key does not exist, leave yearMonthPattern to be null, so that we will pick up the default table value.
+
+ int useOverridesForYearMonthPattern = useOverrides;
+ if (useOverridesForYearMonthPattern == 0)
+ {
+ HKEY hkey = NULL;
+ useOverridesForYearMonthPattern = CAL_NOUSEROVERRIDE;
+ if (WszRegOpenKeyEx(HKEY_CURRENT_USER, W("Control Panel\\International"), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
+ {
+ if (WszRegQueryValueEx(hkey, W("sYearMonth"), 0, NULL, NULL, NULL) == ERROR_SUCCESS)
+ {
+ // The sYearMonth key exists. Call GetLocaleInfo() to read it.
+ useOverridesForYearMonthPattern = 0; // now we can use the overrides
+ }
+ RegCloseKey(hkey);
+ }
+ }
+
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SYEARMONTH, LOCALE_SYEARMONTH | useOverridesForYearMonthPattern, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saYearMonths), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SYEARMONTH");
+ ret = FALSE;
+ }
+
+ // Day & Month Names
+ // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names
+
+ // Day
+ // Note that we're off-by-one since managed starts on sunday and windows starts on monday
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saDayNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SDAYNAME7");
+ ret = FALSE;
+ }
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SABBREVDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevDayNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVDAYNAME7");
+ ret = FALSE;
+ }
+
+ // Month names
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SMONTHNAME1");
+ ret = FALSE;
+ }
+
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthNames), array, NULL);
+ else
+ {
+ _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVMONTHNAME1");
+ ret = FALSE;
+ }
+
+ //
+ // The following LCTYPE are not supported in some platforms. If the call fails,
+ // don't return a failure.
+ //
+ if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SSHORTESTDAYNAME7, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saSuperShortDayNames), array, NULL);
+
+
+ // Gregorian may have genitive month names
+ if (calendar == CAL_GREGORIAN)
+ {
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthGenitiveNames), array, NULL);
+ // else we ignore the error and let managed side copy the normal month names
+ if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthGenitiveNames), array, NULL);
+ // else we ignore the error and let managed side copy the normal month names
+ }
+
+// leap year names are only different for month 6 in Hebrew calendar
+// PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. (Hebrew's the only one that has these)
+
+ // Calendar Parts Names
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SERASTRING, NULL, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saEraNames), array, NULL);
+ // else we set the era in managed code
+ if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SABBREVERASTRING, NULL, &array))
+ SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevEraNames), array, NULL);
+ // else we set the era in managed code
+
+ // PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English
+
+ //
+ // Calendar Era Info
+ // Note that calendar era data (offsets, etc) is hard coded for each calendar since this
+ // data is implementation specific and not dynamic (except perhaps Japanese)
+ //
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+//
+// Get the system two digit year max value for the specified calendar
+//
+FCIMPL1(INT32, CalendarData::nativeGetTwoDigitYearMax, INT32 calendar)
+{
+ FCALL_CONTRACT;
+
+ DWORD dwTwoDigitYearMax = (DWORD) -1;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ WCHAR strName[LOCALE_NAME_MAX_LENGTH];
+
+ // Really we should just be able to pass NULL for the locale name since that
+ // causes the OS to look up the user default locale name. The downlevel APIS could
+ // emulate this as necessary
+ if (NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)) == 0 ||
+ NewApis::GetCalendarInfoEx(strName, calendar, NULL, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER, NULL, 0,&dwTwoDigitYearMax) == 0)
+ {
+ dwTwoDigitYearMax = (DWORD) -1;
+ goto lExit;
+ }
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+
+ return (dwTwoDigitYearMax);
+}
+FCIMPLEND
+
+//
+// nativeGetCalendars
+//
+// Get the list of acceptable calendars for this user/locale
+//
+// Might be a better way to marshal the int[] for calendars
+// We expect the input array to be 23 ints long. We then fill up the first "count" ints and return the count.
+// The caller should then make it a smaller array.
+//
+
+// Perhaps we could do something more like this...
+//U1ARRAYREF rgbOut = (U1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb);
+//memcpyNoGCRefs(rgbOut->GetDirectPointerToNonObjectElements(), rgbKey, cb * sizeof(BYTE));
+//
+//refRetVal = rgbOut;
+//
+//HELPER_METHOD_FRAME_END();
+//return (U1Array*) OBJECTREFToObject(refRetVal);
+
+//
+// struct to help our calendar data enumaration callback
+//
+struct enumCalendarsData
+{
+ int count; // # of strings found so far
+ CALID userOverride; // user override value (if found)
+ INT32* calendarList; // list of calendars found so far
+};
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumCalendarsCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpCalendarInfoString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumCalendarsData* pData = (enumCalendarsData*)lParam;
+
+ // If we had a user override, check to make sure this differs
+ if (pData->userOverride == Calendar)
+ {
+ // Its the same, just return
+ return TRUE;
+ }
+
+ // They're different, add it to our buffer, check we have room
+ if (pData->count < 23)
+ {
+ pData->calendarList[pData->count++] = Calendar;
+ }
+
+ return TRUE;
+}
+
+FCIMPL3(INT32, CalendarData::nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL useOverrides, I4Array* calendarsUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+ int ret = 0;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ I4ARRAYREF calendarsRef;
+ } gc;
+
+ // Dereference our string
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.calendarsRef = (I4ARRAYREF)calendarsUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ int calendarBuffer[23];
+ struct enumCalendarsData data;
+ data.count = 0;
+ data.userOverride = 0;
+ data.calendarList = calendarBuffer;
+
+ // First call GetLocaleInfo if necessary
+ if (useOverrides)
+ {
+ // They want user overrides, see if the user calendar matches the input calendar
+
+ CALID userCalendar = 0;
+ NewApis::GetLocaleInfoEx( gc.localeName->GetBuffer(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
+ (LPWSTR)&userCalendar, sizeof(userCalendar) / sizeof(WCHAR) );
+
+ // If we got a default, then use it as the first calendar
+ if (userCalendar != 0)
+ {
+ data.userOverride = userCalendar;
+ data.calendarList[data.count++] = userCalendar;
+ }
+ }
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumCalendarInfoExEx(EnumCalendarsCallback, gc.localeName->GetBuffer(), ENUM_ALL_CALENDARS, CAL_ICALINTVALUE, (LPARAM)&(data));
+
+ // Copy to the output array
+ for (int i = 0; i < data.count; i++)
+ {
+ (gc.calendarsRef->GetDirectPointerToNonObjectElements())[i] = calendarBuffer[i];
+ }
+
+ ret = data.count;
+ HELPER_METHOD_FRAME_END();
+
+ // Now we have a list of data, return the count
+ return ret;
+}
+FCIMPLEND
+
+//
+// nativeEnumTimeFormats
+//
+// Enumerate all of the time formats (long times) on the system.
+// Windows only has 1 time format so there's nothing like an LCTYPE here.
+//
+// Note that if the locale is the user default locale windows ALWAYS returns the user override value first.
+// (ie: there's no no-user-override option for this API)
+//
+// We reuse the enumData structure since it works for us.
+//
+
+//
+// callback itself
+//
+BOOL CALLBACK EnumTimeFormatsCallback(__in_z LPCWSTR lpTimeFormatString, __in LPARAM lParam)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpTimeFormatString));
+ PRECONDITION(CheckPointer((LPVOID)lParam));
+ } CONTRACTL_END;
+
+ // Cast our data to the right type
+ enumData* pData = (enumData*)lParam;
+
+ // Don't have to worry about user overrides (the enum adds them)
+ // add it to our buffer
+ LPWSTR pStart = pData->stringsBuffer;
+ LPCWSTR pEnd = pData->endOfBuffer;
+ while (pStart < pEnd && *lpTimeFormatString != 0)
+ {
+ *(pStart++) = *(lpTimeFormatString++);
+ }
+
+ // Add a \0
+ if (pStart < pEnd)
+ {
+ *(pStart++) = 0;
+
+ // Did it finish?
+ if (pStart <= pEnd)
+ {
+ // It finished, use it
+ pData->count++;
+ pData->stringsBuffer = pStart;
+ }
+ }
+
+ return TRUE;
+}
+
+//
+// nativeEnumTimeFormats that calls the callback above
+//
+FCIMPL3(Object*, CalendarData::nativeEnumTimeFormats,
+ StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
+ } CONTRACTL_END;
+
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ PTRARRAYREF timeFormatsArray;
+ } gc;
+
+ // Dereference our gc objects
+ gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
+ gc.timeFormatsArray = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
+ WCHAR stringBuffer[512];
+ struct enumData data;
+ data.count = 0;
+ data.userOverride = NULL;
+ data.stringsBuffer = stringBuffer;
+ data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
+
+ // Now call the enumeration API. Work is done by our callback function
+ NewApis::EnumTimeFormatsEx((TIMEFMT_ENUMPROCEX)EnumTimeFormatsCallback, gc.localeName->GetBuffer(), dwFlags, (LPARAM)&data);
+
+ if (data.count > 0)
+ {
+ // Now we need to allocate our stringarray and populate it
+ // Get our array object (will throw, don't have to check it)
+ gc.timeFormatsArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
+
+ LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
+ for(DWORD i = 0; i < (DWORD)data.count; i++) // todo: cast to compile on Mac
+ {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
+ gc.timeFormatsArray->SetAt(i, o);
+
+ buffer += (lstrlenW(buffer) + 1);
+ }
+
+ if(!useUserOverride && data.count > 1)
+ {
+ // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
+ // The override is the first entry if it is overriden.
+ // We can check if we have overrides by checking the GetLocaleInfo with no override
+ // If we do have an override, we don't know if it is a user defined override or if the
+ // user has just selected one of the predefined formats so we can't just remove it
+ // but we can move it down.
+ WCHAR timeFormatNoUserOverride[LOCALE_NAME_MAX_LENGTH];
+ DWORD lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT;
+ lcType |= LOCALE_NOUSEROVERRIDE;
+ int result = NewApis::GetLocaleInfoEx(gc.localeName->GetBuffer(), lcType, timeFormatNoUserOverride, LOCALE_NAME_MAX_LENGTH);
+ if(result != 0)
+ {
+ STRINGREF firstTimeFormat = (STRINGREF)gc.timeFormatsArray->GetAt(0);
+ if(wcscmp(timeFormatNoUserOverride, firstTimeFormat->GetBuffer())!=0)
+ {
+ gc.timeFormatsArray->SetAt(0, gc.timeFormatsArray->GetAt(1));
+ gc.timeFormatsArray->SetAt(1, firstTimeFormat);
+ }
+ }
+ }
+
+ }
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(gc.timeFormatsArray);
+}
+FCIMPLEND
+
+
diff --git a/src/classlibnative/nls/encodingdata.cpp b/src/classlibnative/nls/encodingdata.cpp
new file mode 100644
index 0000000000..bf5c73fd63
--- /dev/null
+++ b/src/classlibnative/nls/encodingdata.cpp
@@ -0,0 +1,682 @@
+// 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.
+
+#include "common.h"
+
+#include <mlang.h>
+
+#include "nlsinfo.h"
+
+//
+// Encoding data tables
+//
+
+//
+// Index an encoding name into an codepage in CodePageDataTable.
+//
+// Please KEEP this table SORTED ALPHABETICALLY! We do a binary search on this array.
+const EncodingDataItem COMNlsInfo::EncodingDataTable[] = {
+#ifdef FEATURE_CORECLR
+ // encoding name, codepage.
+ {"ANSI_X3.4-1968", 20127 },
+ {"ANSI_X3.4-1986", 20127 },
+ {"ascii", 20127 },
+ {"cp367", 20127 },
+ {"cp819", 28591 },
+ {"csASCII", 20127 },
+ {"csISOLatin1", 28591 },
+ {"csUnicode11UTF7", 65000 },
+ {"IBM367", 20127 },
+ {"ibm819", 28591 },
+ {"ISO-10646-UCS-2", 1200 },
+ {"iso-8859-1", 28591 },
+ {"iso-ir-100", 28591 },
+ {"iso-ir-6", 20127 },
+ {"ISO646-US", 20127 },
+ {"iso8859-1", 28591 },
+ {"ISO_646.irv:1991", 20127 },
+ {"iso_8859-1", 28591 },
+ {"iso_8859-1:1987", 28591 },
+ {"l1", 28591 },
+ {"latin1", 28591 },
+ {"ucs-2", 1200 },
+ {"unicode", 1200},
+ {"unicode-1-1-utf-7", 65000 },
+ {"unicode-1-1-utf-8", 65001 },
+ {"unicode-2-0-utf-7", 65000 },
+ {"unicode-2-0-utf-8", 65001 },
+ // People get confused about the FFFE here. We can't change this because it'd break existing apps.
+ // This has been this way for a long time, including in Mlang.
+ {"unicodeFFFE", 1201}, // Big Endian, BOM seems backwards, think of the BOM in little endian order.
+ {"us", 20127 },
+ {"us-ascii", 20127 },
+ {"utf-16", 1200 },
+ {"UTF-16BE", 1201},
+ {"UTF-16LE", 1200},
+ {"utf-32", 12000 },
+ {"UTF-32BE", 12001 },
+ {"UTF-32LE", 12000 },
+ {"utf-7", 65000 },
+ {"utf-8", 65001 },
+ {"x-unicode-1-1-utf-7", 65000 },
+ {"x-unicode-1-1-utf-8", 65001 },
+ {"x-unicode-2-0-utf-7", 65000 },
+ {"x-unicode-2-0-utf-8", 65001 },
+#else
+ // Total Items: 455
+// encoding name, codepage.
+{"437", 437},
+{"ANSI_X3.4-1968", 20127},
+{"ANSI_X3.4-1986", 20127},
+// {L"_autodetect", 50932},
+// {L"_autodetect_all", 50001},
+// {L"_autodetect_kr", 50949},
+{"arabic", 28596},
+{"ascii", 20127},
+{"ASMO-708", 708},
+{"Big5", 950},
+{"Big5-HKSCS", 950},
+{"CCSID00858", 858},
+{"CCSID00924", 20924},
+{"CCSID01140", 1140},
+{"CCSID01141", 1141},
+{"CCSID01142", 1142},
+{"CCSID01143", 1143},
+{"CCSID01144", 1144},
+{"CCSID01145", 1145},
+{"CCSID01146", 1146},
+{"CCSID01147", 1147},
+{"CCSID01148", 1148},
+{"CCSID01149", 1149},
+{"chinese", 936},
+{"cn-big5", 950},
+{"CN-GB", 936},
+{"CP00858", 858},
+{"CP00924", 20924},
+{"CP01140", 1140},
+{"CP01141", 1141},
+{"CP01142", 1142},
+{"CP01143", 1143},
+{"CP01144", 1144},
+{"CP01145", 1145},
+{"CP01146", 1146},
+{"CP01147", 1147},
+{"CP01148", 1148},
+{"CP01149", 1149},
+{"cp037", 37},
+{"cp1025", 21025},
+{"CP1026", 1026},
+{"cp1256", 1256},
+{"CP273", 20273},
+{"CP278", 20278},
+{"CP280", 20280},
+{"CP284", 20284},
+{"CP285", 20285},
+{"cp290", 20290},
+{"cp297", 20297},
+{"cp367", 20127},
+{"cp420", 20420},
+{"cp423", 20423},
+{"cp424", 20424},
+{"cp437", 437},
+{"CP500", 500},
+{"cp50227", 50227},
+ //{L"cp50229", 50229},
+{"cp819", 28591},
+{"cp850", 850},
+{"cp852", 852},
+{"cp855", 855},
+{"cp857", 857},
+{"cp858", 858},
+{"cp860", 860},
+{"cp861", 861},
+{"cp862", 862},
+{"cp863", 863},
+{"cp864", 864},
+{"cp865", 865},
+{"cp866", 866},
+{"cp869", 869},
+{"CP870", 870},
+{"CP871", 20871},
+{"cp875", 875},
+{"cp880", 20880},
+{"CP905", 20905},
+//{L"cp930", 50930},
+//{L"cp933", 50933},
+//{L"cp935", 50935},
+//{L"cp937", 50937},
+//{L"cp939", 50939},
+{"csASCII", 20127},
+{"csbig5", 950},
+{"csEUCKR", 51949},
+{"csEUCPkdFmtJapanese", 51932},
+{"csGB2312", 936},
+{"csGB231280", 936},
+{"csIBM037", 37},
+{"csIBM1026", 1026},
+{"csIBM273", 20273},
+{"csIBM277", 20277},
+{"csIBM278", 20278},
+{"csIBM280", 20280},
+{"csIBM284", 20284},
+{"csIBM285", 20285},
+{"csIBM290", 20290},
+{"csIBM297", 20297},
+{"csIBM420", 20420},
+{"csIBM423", 20423},
+{"csIBM424", 20424},
+{"csIBM500", 500},
+{"csIBM870", 870},
+{"csIBM871", 20871},
+{"csIBM880", 20880},
+{"csIBM905", 20905},
+{"csIBMThai", 20838},
+{"csISO2022JP", 50221},
+{"csISO2022KR", 50225},
+{"csISO58GB231280", 936},
+{"csISOLatin1", 28591},
+{"csISOLatin2", 28592},
+{"csISOLatin3", 28593},
+{"csISOLatin4", 28594},
+{"csISOLatin5", 28599},
+{"csISOLatin9", 28605},
+{"csISOLatinArabic", 28596},
+{"csISOLatinCyrillic", 28595},
+{"csISOLatinGreek", 28597},
+{"csISOLatinHebrew", 28598},
+{"csKOI8R", 20866},
+{"csKSC56011987", 949},
+{"csPC8CodePage437", 437},
+{"csShiftJIS", 932},
+{"csUnicode11UTF7", 65000},
+{"csWindows31J", 932},
+{"cyrillic", 28595},
+{"DIN_66003", 20106},
+{"DOS-720", 720},
+{"DOS-862", 862},
+{"DOS-874", 874},
+{"ebcdic-cp-ar1", 20420},
+{"ebcdic-cp-be", 500},
+{"ebcdic-cp-ca", 37},
+{"ebcdic-cp-ch", 500},
+{"EBCDIC-CP-DK", 20277},
+{"ebcdic-cp-es", 20284},
+{"ebcdic-cp-fi", 20278},
+{"ebcdic-cp-fr", 20297},
+{"ebcdic-cp-gb", 20285},
+{"ebcdic-cp-gr", 20423},
+{"ebcdic-cp-he", 20424},
+{"ebcdic-cp-is", 20871},
+{"ebcdic-cp-it", 20280},
+{"ebcdic-cp-nl", 37},
+{"EBCDIC-CP-NO", 20277},
+{"ebcdic-cp-roece", 870},
+{"ebcdic-cp-se", 20278},
+{"ebcdic-cp-tr", 20905},
+{"ebcdic-cp-us", 37},
+{"ebcdic-cp-wt", 37},
+{"ebcdic-cp-yu", 870},
+{"EBCDIC-Cyrillic", 20880},
+{"ebcdic-de-273+euro", 1141},
+{"ebcdic-dk-277+euro", 1142},
+{"ebcdic-es-284+euro", 1145},
+{"ebcdic-fi-278+euro", 1143},
+{"ebcdic-fr-297+euro", 1147},
+{"ebcdic-gb-285+euro", 1146},
+{"ebcdic-international-500+euro", 1148},
+{"ebcdic-is-871+euro", 1149},
+{"ebcdic-it-280+euro", 1144},
+{"EBCDIC-JP-kana", 20290},
+{"ebcdic-Latin9--euro", 20924},
+{"ebcdic-no-277+euro", 1142},
+{"ebcdic-se-278+euro", 1143},
+{"ebcdic-us-37+euro", 1140},
+{"ECMA-114", 28596},
+{"ECMA-118", 28597},
+{"ELOT_928", 28597},
+{"euc-cn", 51936},
+{"euc-jp", 51932},
+{"euc-kr", 51949},
+{"Extended_UNIX_Code_Packed_Format_for_Japanese", 51932},
+{"GB18030", 54936},
+{"GB2312", 936},
+{"GB2312-80", 936},
+{"GB231280", 936},
+{"GBK", 936},
+{"GB_2312-80", 936},
+{"German", 20106},
+{"greek", 28597},
+{"greek8", 28597},
+{"hebrew", 28598},
+{"hz-gb-2312", 52936},
+{"IBM-Thai", 20838},
+{"IBM00858", 858},
+{"IBM00924", 20924},
+{"IBM01047", 1047},
+{"IBM01140", 1140},
+{"IBM01141", 1141},
+{"IBM01142", 1142},
+{"IBM01143", 1143},
+{"IBM01144", 1144},
+{"IBM01145", 1145},
+{"IBM01146", 1146},
+{"IBM01147", 1147},
+{"IBM01148", 1148},
+{"IBM01149", 1149},
+{"IBM037", 37},
+{"IBM1026", 1026},
+{"IBM273", 20273},
+{"IBM277", 20277},
+{"IBM278", 20278},
+{"IBM280", 20280},
+{"IBM284", 20284},
+{"IBM285", 20285},
+{"IBM290", 20290},
+{"IBM297", 20297},
+{"IBM367", 20127},
+{"IBM420", 20420},
+{"IBM423", 20423},
+{"IBM424", 20424},
+{"IBM437", 437},
+{"IBM500", 500},
+{"ibm737", 737},
+{"ibm775", 775},
+{"ibm819", 28591},
+{"IBM850", 850},
+{"IBM852", 852},
+{"IBM855", 855},
+{"IBM857", 857},
+{"IBM860", 860},
+{"IBM861", 861},
+{"IBM862", 862},
+{"IBM863", 863},
+{"IBM864", 864},
+{"IBM865", 865},
+{"IBM866", 866},
+{"IBM869", 869},
+{"IBM870", 870},
+{"IBM871", 20871},
+{"IBM880", 20880},
+{"IBM905", 20905},
+{"irv", 20105},
+{"ISO-10646-UCS-2", 1200},
+{"iso-2022-jp", 50220},
+{"iso-2022-jpeuc", 51932},
+{"iso-2022-kr", 50225},
+{"iso-2022-kr-7", 50225},
+{"iso-2022-kr-7bit", 50225},
+{"iso-2022-kr-8", 51949},
+{"iso-2022-kr-8bit", 51949},
+{"iso-8859-1", 28591},
+{"iso-8859-11", 874},
+{"iso-8859-13", 28603},
+{"iso-8859-15", 28605},
+{"iso-8859-2", 28592},
+{"iso-8859-3", 28593},
+{"iso-8859-4", 28594},
+{"iso-8859-5", 28595},
+{"iso-8859-6", 28596},
+{"iso-8859-7", 28597},
+{"iso-8859-8", 28598},
+{"ISO-8859-8 Visual", 28598},
+{"iso-8859-8-i", 38598},
+{"iso-8859-9", 28599},
+{"iso-ir-100", 28591},
+{"iso-ir-101", 28592},
+{"iso-ir-109", 28593},
+{"iso-ir-110", 28594},
+{"iso-ir-126", 28597},
+{"iso-ir-127", 28596},
+{"iso-ir-138", 28598},
+{"iso-ir-144", 28595},
+{"iso-ir-148", 28599},
+{"iso-ir-149", 949},
+{"iso-ir-58", 936},
+{"iso-ir-6", 20127},
+{"ISO646-US", 20127},
+{"iso8859-1", 28591},
+{"iso8859-2", 28592},
+{"ISO_646.irv:1991", 20127},
+{"iso_8859-1", 28591},
+{"ISO_8859-15", 28605},
+{"iso_8859-1:1987", 28591},
+{"iso_8859-2", 28592},
+{"iso_8859-2:1987", 28592},
+{"ISO_8859-3", 28593},
+{"ISO_8859-3:1988", 28593},
+{"ISO_8859-4", 28594},
+{"ISO_8859-4:1988", 28594},
+{"ISO_8859-5", 28595},
+{"ISO_8859-5:1988", 28595},
+{"ISO_8859-6", 28596},
+{"ISO_8859-6:1987", 28596},
+{"ISO_8859-7", 28597},
+{"ISO_8859-7:1987", 28597},
+{"ISO_8859-8", 28598},
+{"ISO_8859-8:1988", 28598},
+{"ISO_8859-9", 28599},
+{"ISO_8859-9:1989", 28599},
+{"Johab", 1361},
+{"koi", 20866},
+{"koi8", 20866},
+{"koi8-r", 20866},
+{"koi8-ru", 21866},
+{"koi8-u", 21866},
+{"koi8r", 20866},
+{"korean", 949},
+{"ks-c-5601", 949},
+{"ks-c5601", 949},
+{"KSC5601", 949},
+{"KSC_5601", 949},
+{"ks_c_5601", 949},
+{"ks_c_5601-1987", 949},
+{"ks_c_5601-1989", 949},
+{"ks_c_5601_1987", 949},
+{"l1", 28591},
+{"l2", 28592},
+{"l3", 28593},
+{"l4", 28594},
+{"l5", 28599},
+{"l9", 28605},
+{"latin1", 28591},
+{"latin2", 28592},
+{"latin3", 28593},
+{"latin4", 28594},
+{"latin5", 28599},
+{"latin9", 28605},
+{"logical", 28598},
+{"macintosh", 10000},
+{"ms_Kanji", 932},
+{"Norwegian", 20108},
+{"NS_4551-1", 20108},
+{"PC-Multilingual-850+euro", 858},
+{"SEN_850200_B", 20107},
+{"shift-jis", 932},
+{"shift_jis", 932},
+{"sjis", 932},
+{"Swedish", 20107},
+{"TIS-620", 874},
+{"ucs-2", 1200},
+{"unicode", 1200},
+{"unicode-1-1-utf-7", 65000},
+{"unicode-1-1-utf-8", 65001},
+{"unicode-2-0-utf-7", 65000},
+{"unicode-2-0-utf-8", 65001},
+// People get confused about the FFFE here. We can't change this because it'd break existing apps.
+// This has been this way for a long time, including in Mlang.
+{"unicodeFFFE", 1201}, // Big Endian, BOM seems backwards, think of the BOM in little endian order.
+{"us", 20127},
+{"us-ascii", 20127},
+{"utf-16", 1200},
+{"UTF-16BE", 1201},
+{"UTF-16LE", 1200},
+{"utf-32", 12000},
+{"UTF-32BE", 12001},
+{"UTF-32LE", 12000},
+{"utf-7", 65000},
+{"utf-8", 65001},
+{"visual", 28598},
+{"windows-1250", 1250},
+{"windows-1251", 1251},
+{"windows-1252", 1252},
+{"windows-1253", 1253},
+{"Windows-1254", 1254},
+{"windows-1255", 1255},
+{"windows-1256", 1256},
+{"windows-1257", 1257},
+{"windows-1258", 1258},
+{"windows-874", 874},
+{"x-ansi", 1252},
+{"x-Chinese-CNS", 20000},
+{"x-Chinese-Eten", 20002},
+{"x-cp1250", 1250},
+{"x-cp1251", 1251},
+{"x-cp20001", 20001},
+{"x-cp20003", 20003},
+{"x-cp20004", 20004},
+{"x-cp20005", 20005},
+{"x-cp20261", 20261},
+{"x-cp20269", 20269},
+{"x-cp20936", 20936},
+{"x-cp20949", 20949},
+{"x-cp50227", 50227},
+//{L"x-cp50229", 50229},
+//{L"X-EBCDIC-JapaneseAndUSCanada", 50931},
+{"X-EBCDIC-KoreanExtended", 20833},
+{"x-euc", 51932},
+{"x-euc-cn", 51936},
+{"x-euc-jp", 51932},
+{"x-Europa", 29001},
+{"x-IA5", 20105},
+{"x-IA5-German", 20106},
+{"x-IA5-Norwegian", 20108},
+{"x-IA5-Swedish", 20107},
+{"x-iscii-as", 57006},
+{"x-iscii-be", 57003},
+{"x-iscii-de", 57002},
+{"x-iscii-gu", 57010},
+{"x-iscii-ka", 57008},
+{"x-iscii-ma", 57009},
+{"x-iscii-or", 57007},
+{"x-iscii-pa", 57011},
+{"x-iscii-ta", 57004},
+{"x-iscii-te", 57005},
+{"x-mac-arabic", 10004},
+{"x-mac-ce", 10029},
+{"x-mac-chinesesimp", 10008},
+{"x-mac-chinesetrad", 10002},
+{"x-mac-croatian", 10082},
+{"x-mac-cyrillic", 10007},
+{"x-mac-greek", 10006},
+{"x-mac-hebrew", 10005},
+{"x-mac-icelandic", 10079},
+{"x-mac-japanese", 10001},
+{"x-mac-korean", 10003},
+{"x-mac-romanian", 10010},
+{"x-mac-thai", 10021},
+{"x-mac-turkish", 10081},
+{"x-mac-ukrainian", 10017},
+{"x-ms-cp932", 932},
+{"x-sjis", 932},
+{"x-unicode-1-1-utf-7", 65000},
+{"x-unicode-1-1-utf-8", 65001},
+{"x-unicode-2-0-utf-7", 65000},
+{"x-unicode-2-0-utf-8", 65001},
+{"x-x-big5", 950},
+
+#endif // FEATURE_CORECLR
+
+};
+
+const int COMNlsInfo::m_nEncodingDataTableItems =
+ sizeof(COMNlsInfo::EncodingDataTable)/sizeof(EncodingDataItem);
+
+// Working set optimization:
+// 1. code page, family code page stored as unsigned short
+// 2. if web/header/body names are the same, only web name is stored; otherwise, we store "|webname|headername|bodyname"
+// 3. Move flags before names to fill gap on 64-bit platforms
+
+#define MapCodePageDataItem(cp, fcp, names, flags) { cp, fcp, flags, names }
+//
+// Information about codepages.
+//
+const CodePageDataItem COMNlsInfo::CodePageDataTable[] = {
+
+#ifdef FEATURE_CORECLR
+
+// Total Items:
+// code page, family code page, web name, header name, body name, flags
+
+ MapCodePageDataItem( 1200, 1200, "utf-16", MIMECONTF_SAVABLE_BROWSER), // "Unicode"
+ MapCodePageDataItem( 1201, 1200, "utf-16BE", 0), // Big Endian, old FFFE BOM seems backwards, think of the BOM in little endian order.
+ MapCodePageDataItem( 12000, 1200, "utf-32", 0), // "Unicode (UTF-32)"
+ MapCodePageDataItem( 12001, 1200, "utf-32BE", 0), // "Unicode (UTF-32 Big Endian)"
+ MapCodePageDataItem( 20127, 1252, "us-ascii", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "US-ASCII"
+ MapCodePageDataItem( 28591, 1252, "iso-8859-1", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Western European (ISO)"
+ MapCodePageDataItem( 65000, 1200, "utf-7", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Unicode (UTF-7)"
+ MapCodePageDataItem( 65001, 1200, "utf-8", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Unicode (UTF-8)"
+
+#else //FEATURE_CORECLR
+
+// Total Items: 146
+// code page, family code page, web name, header name, body name, flags
+
+
+ MapCodePageDataItem( 37, 1252, "IBM037", 0), // "IBM EBCDIC (US-Canada)"
+ MapCodePageDataItem( 437, 1252, "IBM437", 0), // "OEM United States"
+ MapCodePageDataItem( 500, 1252, "IBM500", 0), // "IBM EBCDIC (International)"
+ MapCodePageDataItem( 708, 1256, "ASMO-708", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Arabic (ASMO 708)"
+ MapCodePageDataItem( 720, 1256, "DOS-720", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Arabic (DOS)"
+ MapCodePageDataItem( 737, 1253, "ibm737", 0), // "Greek (DOS)"
+ MapCodePageDataItem( 775, 1257, "ibm775", 0), // "Baltic (DOS)"
+ MapCodePageDataItem( 850, 1252, "ibm850", 0), // "Western European (DOS)"
+ MapCodePageDataItem( 852, 1250, "ibm852", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Central European (DOS)"
+ MapCodePageDataItem( 855, 1252, "IBM855", 0), // "OEM Cyrillic"
+ MapCodePageDataItem( 857, 1254, "ibm857", 0), // "Turkish (DOS)"
+ MapCodePageDataItem( 858, 1252, "IBM00858", 0), // "OEM Multilingual Latin I"
+ MapCodePageDataItem( 860, 1252, "IBM860", 0), // "Portuguese (DOS)"
+ MapCodePageDataItem( 861, 1252, "ibm861", 0), // "Icelandic (DOS)"
+ MapCodePageDataItem( 862, 1255, "DOS-862", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Hebrew (DOS)"
+ MapCodePageDataItem( 863, 1252, "IBM863", 0), // "French Canadian (DOS)"
+ MapCodePageDataItem( 864, 1256, "IBM864", 0), // "Arabic (864)"
+ MapCodePageDataItem( 865, 1252, "IBM865", 0), // "Nordic (DOS)"
+ MapCodePageDataItem( 866, 1251, "cp866", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Cyrillic (DOS)"
+ MapCodePageDataItem( 869, 1253, "ibm869", 0), // "Greek, Modern (DOS)"
+ MapCodePageDataItem( 870, 1250, "IBM870", 0), // "IBM EBCDIC (Multilingual Latin-2)"
+ MapCodePageDataItem( 874, 874, "windows-874", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Thai (Windows)"
+ MapCodePageDataItem( 875, 1253, "cp875", 0), // "IBM EBCDIC (Greek Modern)"
+ MapCodePageDataItem( 932, 932, "|shift_jis|iso-2022-jp|iso-2022-jp", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Japanese (Shift-JIS)"
+ MapCodePageDataItem( 936, 936, "gb2312", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Chinese Simplified (GB2312)"
+ MapCodePageDataItem( 949, 949, "ks_c_5601-1987", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Korean"
+ MapCodePageDataItem( 950, 950, "big5", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Chinese Traditional (Big5)"
+ MapCodePageDataItem( 1026, 1254, "IBM1026", 0), // "IBM EBCDIC (Turkish Latin-5)"
+ MapCodePageDataItem( 1047, 1252, "IBM01047", 0), // "IBM Latin-1"
+ MapCodePageDataItem( 1140, 1252, "IBM01140", 0), // "IBM EBCDIC (US-Canada-Euro)"
+ MapCodePageDataItem( 1141, 1252, "IBM01141", 0), // "IBM EBCDIC (Germany-Euro)"
+ MapCodePageDataItem( 1142, 1252, "IBM01142", 0), // "IBM EBCDIC (Denmark-Norway-Euro)"
+ MapCodePageDataItem( 1143, 1252, "IBM01143", 0), // "IBM EBCDIC (Finland-Sweden-Euro)"
+ MapCodePageDataItem( 1144, 1252, "IBM01144", 0), // "IBM EBCDIC (Italy-Euro)"
+ MapCodePageDataItem( 1145, 1252, "IBM01145", 0), // "IBM EBCDIC (Spain-Euro)"
+ MapCodePageDataItem( 1146, 1252, "IBM01146", 0), // "IBM EBCDIC (UK-Euro)"
+ MapCodePageDataItem( 1147, 1252, "IBM01147", 0), // "IBM EBCDIC (France-Euro)"
+ MapCodePageDataItem( 1148, 1252, "IBM01148", 0), // "IBM EBCDIC (International-Euro)"
+ MapCodePageDataItem( 1149, 1252, "IBM01149", 0), // "IBM EBCDIC (Icelandic-Euro)"
+ MapCodePageDataItem( 1200, 1200, "utf-16", MIMECONTF_SAVABLE_BROWSER), // "Unicode"
+ MapCodePageDataItem( 1201, 1200, "utf-16BE", 0), // Big Endian, old FFFE BOM seems backwards, think of the BOM in little endian order.
+ MapCodePageDataItem( 1250, 1250, "|windows-1250|windows-1250|iso-8859-2", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Central European (Windows)"
+ MapCodePageDataItem( 1251, 1251, "|windows-1251|windows-1251|koi8-r", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Cyrillic (Windows)"
+ MapCodePageDataItem( 1252, 1252, "|Windows-1252|Windows-1252|iso-8859-1", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Western European (Windows)"
+ MapCodePageDataItem( 1253, 1253, "|windows-1253|windows-1253|iso-8859-7", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Greek (Windows)"
+ MapCodePageDataItem( 1254, 1254, "|windows-1254|windows-1254|iso-8859-9", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Turkish (Windows)"
+ MapCodePageDataItem( 1255, 1255, "windows-1255", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Hebrew (Windows)"
+ MapCodePageDataItem( 1256, 1256, "windows-1256", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Arabic (Windows)"
+ MapCodePageDataItem( 1257, 1257, "windows-1257", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Baltic (Windows)"
+ MapCodePageDataItem( 1258, 1258, "windows-1258", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Vietnamese (Windows)"
+ MapCodePageDataItem( 1361, 949, "Johab", 0), // "Korean (Johab)"
+ MapCodePageDataItem( 10000, 1252, "macintosh", 0), // "Western European (Mac)"
+ MapCodePageDataItem( 10001, 932, "x-mac-japanese", 0), // "Japanese (Mac)"
+ MapCodePageDataItem( 10002, 950, "x-mac-chinesetrad", 0), // "Chinese Traditional (Mac)"
+ MapCodePageDataItem( 10003, 949, "x-mac-korean", 0), // "Korean (Mac)"
+ MapCodePageDataItem( 10004, 1256, "x-mac-arabic", 0), // "Arabic (Mac)"
+ MapCodePageDataItem( 10005, 1255, "x-mac-hebrew", 0), // "Hebrew (Mac)"
+ MapCodePageDataItem( 10006, 1253, "x-mac-greek", 0), // "Greek (Mac)"
+ MapCodePageDataItem( 10007, 1251, "x-mac-cyrillic", 0), // "Cyrillic (Mac)"
+ MapCodePageDataItem( 10008, 936, "x-mac-chinesesimp", 0), // "Chinese Simplified (Mac)"
+ MapCodePageDataItem( 10010, 1250, "x-mac-romanian", 0), // "Romanian (Mac)"
+ MapCodePageDataItem( 10017, 1251, "x-mac-ukrainian", 0), // "Ukrainian (Mac)"
+ MapCodePageDataItem( 10021, 874, "x-mac-thai", 0), // "Thai (Mac)"
+ MapCodePageDataItem( 10029, 1250, "x-mac-ce", 0), // "Central European (Mac)"
+ MapCodePageDataItem( 10079, 1252, "x-mac-icelandic", 0), // "Icelandic (Mac)"
+ MapCodePageDataItem( 10081, 1254, "x-mac-turkish", 0), // "Turkish (Mac)"
+ MapCodePageDataItem( 10082, 1250, "x-mac-croatian", 0), // "Croatian (Mac)"
+ MapCodePageDataItem( 12000, 1200, "utf-32", 0), // "Unicode (UTF-32)"
+ MapCodePageDataItem( 12001, 1200, "utf-32BE", 0), // "Unicode (UTF-32 Big Endian)"
+ MapCodePageDataItem( 20000, 950, "x-Chinese-CNS", 0), // "Chinese Traditional (CNS)"
+ MapCodePageDataItem( 20001, 950, "x-cp20001", 0), // "TCA Taiwan"
+ MapCodePageDataItem( 20002, 950, "x-Chinese-Eten", 0), // "Chinese Traditional (Eten)"
+ MapCodePageDataItem( 20003, 950, "x-cp20003", 0), // "IBM5550 Taiwan"
+ MapCodePageDataItem( 20004, 950, "x-cp20004", 0), // "TeleText Taiwan"
+ MapCodePageDataItem( 20005, 950, "x-cp20005", 0), // "Wang Taiwan"
+ MapCodePageDataItem( 20105, 1252, "x-IA5", 0), // "Western European (IA5)"
+ MapCodePageDataItem( 20106, 1252, "x-IA5-German", 0), // "German (IA5)"
+ MapCodePageDataItem( 20107, 1252, "x-IA5-Swedish", 0), // "Swedish (IA5)"
+ MapCodePageDataItem( 20108, 1252, "x-IA5-Norwegian", 0), // "Norwegian (IA5)"
+ MapCodePageDataItem( 20127, 1252, "us-ascii", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "US-ASCII"
+ MapCodePageDataItem( 20261, 1252, "x-cp20261", 0), // "T.61"
+ MapCodePageDataItem( 20269, 1252, "x-cp20269", 0), // "ISO-6937"
+ MapCodePageDataItem( 20273, 1252, "IBM273", 0), // "IBM EBCDIC (Germany)"
+ MapCodePageDataItem( 20277, 1252, "IBM277", 0), // "IBM EBCDIC (Denmark-Norway)"
+ MapCodePageDataItem( 20278, 1252, "IBM278", 0), // "IBM EBCDIC (Finland-Sweden)"
+ MapCodePageDataItem( 20280, 1252, "IBM280", 0), // "IBM EBCDIC (Italy)"
+ MapCodePageDataItem( 20284, 1252, "IBM284", 0), // "IBM EBCDIC (Spain)"
+ MapCodePageDataItem( 20285, 1252, "IBM285", 0), // "IBM EBCDIC (UK)"
+ MapCodePageDataItem( 20290, 932, "IBM290", 0), // "IBM EBCDIC (Japanese katakana)"
+ MapCodePageDataItem( 20297, 1252, "IBM297", 0), // "IBM EBCDIC (France)"
+ MapCodePageDataItem( 20420, 1256, "IBM420", 0), // "IBM EBCDIC (Arabic)"
+ MapCodePageDataItem( 20423, 1253, "IBM423", 0), // "IBM EBCDIC (Greek)"
+ MapCodePageDataItem( 20424, 1255, "IBM424", 0), // "IBM EBCDIC (Hebrew)"
+ MapCodePageDataItem( 20833, 949, "x-EBCDIC-KoreanExtended", 0), // "IBM EBCDIC (Korean Extended)"
+ MapCodePageDataItem( 20838, 874, "IBM-Thai", 0), // "IBM EBCDIC (Thai)"
+ MapCodePageDataItem( 20866, 1251, "koi8-r", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Cyrillic (KOI8-R)"
+ MapCodePageDataItem( 20871, 1252, "IBM871", 0), // "IBM EBCDIC (Icelandic)"
+ MapCodePageDataItem( 20880, 1251, "IBM880", 0), // "IBM EBCDIC (Cyrillic Russian)"
+ MapCodePageDataItem( 20905, 1254, "IBM905", 0), // "IBM EBCDIC (Turkish)"
+ MapCodePageDataItem( 20924, 1252, "IBM00924", 0), // "IBM Latin-1"
+ MapCodePageDataItem( 20932, 932, "EUC-JP", 0), // "Japanese (JIS 0208-1990 and 0212-1990)"
+ MapCodePageDataItem( 20936, 936, "x-cp20936", 0), // "Chinese Simplified (GB2312-80)"
+ MapCodePageDataItem( 20949, 949, "x-cp20949", 0), // "Korean Wansung"
+ MapCodePageDataItem( 21025, 1251, "cp1025", 0), // "IBM EBCDIC (Cyrillic Serbian-Bulgarian)"
+ MapCodePageDataItem( 21866, 1251, "koi8-u", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Cyrillic (KOI8-U)"
+ MapCodePageDataItem( 28591, 1252, "iso-8859-1", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Western European (ISO)"
+ MapCodePageDataItem( 28592, 1250, "iso-8859-2", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Central European (ISO)"
+ MapCodePageDataItem( 28593, 1254, "iso-8859-3", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Latin 3 (ISO)"
+ MapCodePageDataItem( 28594, 1257, "iso-8859-4", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Baltic (ISO)"
+ MapCodePageDataItem( 28595, 1251, "iso-8859-5", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Cyrillic (ISO)"
+ MapCodePageDataItem( 28596, 1256, "iso-8859-6", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Arabic (ISO)"
+ MapCodePageDataItem( 28597, 1253, "iso-8859-7", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Greek (ISO)"
+ MapCodePageDataItem( 28598, 1255, "iso-8859-8", MIMECONTF_BROWSER | MIMECONTF_SAVABLE_BROWSER), // "Hebrew (ISO-Visual)"
+ MapCodePageDataItem( 28599, 1254, "iso-8859-9", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Turkish (ISO)"
+ MapCodePageDataItem( 28603, 1257, "iso-8859-13", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Estonian (ISO)"
+ MapCodePageDataItem( 28605, 1252, "iso-8859-15", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Latin 9 (ISO)"
+ MapCodePageDataItem( 29001, 1252, "x-Europa", 0), // "Europa"
+ MapCodePageDataItem( 38598, 1255, "iso-8859-8-i", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Hebrew (ISO-Logical)"
+ MapCodePageDataItem( 50220, 932, "iso-2022-jp", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Japanese (JIS)"
+ MapCodePageDataItem( 50221, 932, "|csISO2022JP|iso-2022-jp|iso-2022-jp", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Japanese (JIS-Allow 1 byte Kana)"
+ MapCodePageDataItem( 50222, 932, "iso-2022-jp", 0), // "Japanese (JIS-Allow 1 byte Kana - SO/SI)"
+ MapCodePageDataItem( 50225, 949, "|iso-2022-kr|euc-kr|iso-2022-kr", MIMECONTF_MAILNEWS), // "Korean (ISO)"
+ MapCodePageDataItem( 50227, 936, "x-cp50227", 0), // "Chinese Simplified (ISO-2022)"
+//MapCodePageDataItem( 50229, 950, L"x-cp50229", L"x-cp50229", L"x-cp50229", 0}, // "Chinese Traditional (ISO-2022)"
+//MapCodePageDataItem( 50930, 932, L"cp930", L"cp930", L"cp930", 0}, // "IBM EBCDIC (Japanese and Japanese Katakana)"
+//MapCodePageDataItem( 50931, 932, L"x-EBCDIC-JapaneseAndUSCanada", L"x-EBCDIC-JapaneseAndUSCanada", L"x-EBCDIC-JapaneseAndUSCanada", 0}, // "IBM EBCDIC (Japanese and US-Canada)"
+//MapCodePageDataItem( 50933, 949, L"cp933", L"cp933", L"cp933", 0}, // "IBM EBCDIC (Korean and Korean Extended)"
+//MapCodePageDataItem( 50935, 936, L"cp935", L"cp935", L"cp935", 0}, // "IBM EBCDIC (Simplified Chinese)"
+//MapCodePageDataItem( 50937, 950, L"cp937", L"cp937", L"cp937", 0}, // "IBM EBCDIC (Traditional Chinese)"
+//MapCodePageDataItem( 50939, 932, L"cp939", L"cp939", L"cp939", 0}, // "IBM EBCDIC (Japanese and Japanese-Latin)"
+ MapCodePageDataItem( 51932, 932, "euc-jp", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Japanese (EUC)"
+ MapCodePageDataItem( 51936, 936, "EUC-CN", 0), // "Chinese Simplified (EUC)"
+ MapCodePageDataItem( 51949, 949, "euc-kr", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Korean (EUC)"
+ MapCodePageDataItem( 52936, 936, "hz-gb-2312", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Chinese Simplified (HZ)"
+ MapCodePageDataItem( 54936, 936, "GB18030", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Chinese Simplified (GB18030)"
+ MapCodePageDataItem( 57002, 57002, "x-iscii-de", 0), // "ISCII Devanagari"
+ MapCodePageDataItem( 57003, 57003, "x-iscii-be", 0), // "ISCII Bengali"
+ MapCodePageDataItem( 57004, 57004, "x-iscii-ta", 0), // "ISCII Tamil"
+ MapCodePageDataItem( 57005, 57005, "x-iscii-te", 0), // "ISCII Telugu"
+ MapCodePageDataItem( 57006, 57006, "x-iscii-as", 0), // "ISCII Assamese"
+ MapCodePageDataItem( 57007, 57007, "x-iscii-or", 0), // "ISCII Oriya"
+ MapCodePageDataItem( 57008, 57008, "x-iscii-ka", 0), // "ISCII Kannada"
+ MapCodePageDataItem( 57009, 57009, "x-iscii-ma", 0), // "ISCII Malayalam"
+ MapCodePageDataItem( 57010, 57010, "x-iscii-gu", 0), // "ISCII Gujarati"
+ MapCodePageDataItem( 57011, 57011, "x-iscii-pa", 0), // "ISCII Punjabi"
+ MapCodePageDataItem( 65000, 1200, "utf-7", MIMECONTF_MAILNEWS | MIMECONTF_SAVABLE_MAILNEWS), // "Unicode (UTF-7)"
+ MapCodePageDataItem( 65001, 1200, "utf-8", MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER), // "Unicode (UTF-8)"
+#endif // FEATURE_CORECLR
+
+ // End of data.
+ MapCodePageDataItem( 0, 0, NULL, 0),
+
+};
+
+const int COMNlsInfo::m_nCodePageTableItems =
+ sizeof(COMNlsInfo::CodePageDataTable)/sizeof(CodePageDataItem);
+
diff --git a/src/classlibnative/nls/nlsinfo.cpp b/src/classlibnative/nls/nlsinfo.cpp
new file mode 100644
index 0000000000..864f998d1f
--- /dev/null
+++ b/src/classlibnative/nls/nlsinfo.cpp
@@ -0,0 +1,3476 @@
+// 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.
+////////////////////////////////////////////////////////////////////////////
+//
+// Class: NLSInfo
+//
+
+//
+// Purpose: This module implements the methods of the COMNlsInfo
+// class. These methods are the helper functions for the
+// Locale class.
+//
+// Date: August 12, 1998
+//
+////////////////////////////////////////////////////////////////////////////
+
+//
+// Include Files.
+//
+#include "common.h"
+#include "object.h"
+#include "excep.h"
+#include "vars.hpp"
+#include "interoputil.h"
+#include "corhost.h"
+
+#include <winnls.h>
+
+#include "utilcode.h"
+#include "frames.h"
+#include "field.h"
+#include "metasig.h"
+#include "nls.h"
+#include "nlsinfo.h"
+#include "nlstable.h"
+
+//#include <mlang.h>
+#include "sortversioning.h"
+
+#include "newapis.h"
+
+//
+// Constant Declarations.
+//
+
+#ifndef COMPARE_OPTIONS_IGNORECASE
+#define COMPARE_OPTIONS_IGNORECASE 0x00000001
+#endif
+
+#ifndef LOCALE_SNAME
+#define LOCALE_SNAME 0x0000005c
+#endif
+
+#ifndef LOCALE_SNAN
+#define LOCALE_SNAN 0x00000069
+#endif
+
+#ifndef LOCALE_SPOSINFINITY
+#define LOCALE_SPOSINFINITY 0x0000006a
+#endif
+
+#ifndef LOCALE_SNEGINFINITY
+#define LOCALE_SNEGINFINITY 0x0000006b
+#endif
+
+#ifndef LOCALE_SPARENT
+#define LOCALE_SPARENT 0x0000006d
+#endif
+
+#ifndef LOCALE_SCONSOLEFALLBACKNAME
+#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console
+#endif
+
+#ifndef LOCALE_SISO3166CTRYNAME2
+#define LOCALE_SISO3166CTRYNAME2 0x00000068
+#endif
+
+#ifndef LOCALE_SISO639LANGNAME2
+#define LOCALE_SISO639LANGNAME2 0x00000067
+#endif
+
+#ifndef LOCALE_SSHORTESTDAYNAME1
+#define LOCALE_SSHORTESTDAYNAME1 0x00000060
+#endif
+
+// Windows 7 LCTypes
+#ifndef LOCALE_INEUTRAL
+#define LOCALE_INEUTRAL 0x00000071 // Returns 0 for specific cultures, 1 for neutral cultures.
+#endif
+
+#ifndef LCMAP_TITLECASE
+#define LCMAP_TITLECASE 0x00000300 // Title Case Letters
+#endif
+
+// Windows 8 LCTypes
+#ifndef LCMAP_SORTHANDLE
+#define LCMAP_SORTHANDLE 0x20000000
+#endif
+
+#ifndef LCMAP_HASH
+#define LCMAP_HASH 0x00040000
+#endif
+
+#ifndef LOCALE_REPLACEMENT
+#define LOCALE_REPLACEMENT 0x00000008 // locales that replace shipped locales (callback flag only)
+#endif // LOCALE_REPLACEMENT
+
+#define LOCALE_MAX_STRING_SIZE 530 // maximum sice of LOCALE_SKEYBOARDSTOINSTALL, currently 5 "long" + 2 "short" keyboard signatures (YI + 3).
+
+#define MAX_STRING_VALUE 512
+
+// TODO: NLS Arrowhead -Be nice if we could depend more on the OS for this
+// Language ID for CHT (Taiwan)
+#define LANGID_ZH_TW 0x0404
+// Language ID for CHT (Hong-Kong)
+#define LANGID_ZH_HK 0x0c04
+#define REGION_NAME_0404 W("\x53f0\x7063")
+#if BIGENDIAN
+#define INTERNATIONAL_CURRENCY_SYMBOL W("\x00a4")
+#else
+#define INTERNATIONAL_CURRENCY_SYMBOL W("\xa400")
+#endif
+
+inline BOOL IsCustomCultureId(LCID lcid)
+{
+ return (lcid == LOCALE_CUSTOM_DEFAULT || lcid == LOCALE_CUSTOM_UNSPECIFIED);
+}
+
+#ifndef FEATURE_COREFX_GLOBALIZATION
+//
+// Normalization Implementation
+//
+#define NORMALIZATION_DLL MAKEDLLNAME(W("normalization"))
+HMODULE COMNlsInfo::m_hNormalization = NULL;
+PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL;
+PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL;
+PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL;
+#endif // FEATURE_COREFX_GLOBALIZATION
+
+#if FEATURE_CODEPAGES_FILE
+/*============================nativeCreateOpenFileMapping============================
+**Action: Create or open a named memory file mapping.
+**Returns: Pointer to named section, or NULL if failed
+**Arguments:
+** StringObject* inSectionName - name of section to open/create
+** int inBytesToAllocate - desired size of memory section in bytes
+** We use the last 4 bytes (must be aligned, so only choose
+** inBytesToAllocate in multiples of 4) to indicate if the
+** section is set or not. AFTER section is initialized, set
+** those 4 bytes to non-0, otherwise you'll get get new
+** heap memory all the time.
+** HANDLE* mappedFile - is the handle of the memory mapped file. this is
+** out parameter.
+**
+** NOTE: We'll try to open the same object, so we can share names. We don't lock
+** though, so 2 thread could get the same object, but thread 1 might not
+** have initialized it yet.
+**
+** NOTE: For NT you should add a Global\ to the beginning of the name if you
+** want to share it machine wide.
+**
+==============================================================================*/
+FCIMPL3(LPVOID, COMNlsInfo::nativeCreateOpenFileMapping,
+ StringObject* inSectionNameUNSAFE, int inBytesToAllocate, HANDLE *mappedFile)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(inSectionNameUNSAFE));
+ PRECONDITION(inBytesToAllocate % 4 == 0);
+ PRECONDITION(inBytesToAllocate > 0);
+ PRECONDITION(CheckPointer(mappedFile));
+ } CONTRACTL_END;
+
+ // Need a place for our result
+ LPVOID pResult = NULL;
+
+ STRINGREF inString(inSectionNameUNSAFE);
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(inString);
+
+ _ASSERTE(inBytesToAllocate % 4 == 0); // Expected 4 bytes boundaries so we don't get unaligned
+ _ASSERTE(inBytesToAllocate > 0); // Pointless to have <=0 allocation
+
+ StackSString inNameStackBuffer (inString->GetBuffer());
+ pResult = NLSTable::OpenOrCreateMemoryMapping((LPCWSTR)inNameStackBuffer, inBytesToAllocate, mappedFile);
+
+ // Worst case allocate some memory, use holder
+ // if (pResult == NULL) pResult = new BYTE[inBytesToAllocate];
+ if (pResult == NULL)
+ {
+ // Need to use a NewHolder
+ NewArrayHolder<BYTE> holder (new BYTE[inBytesToAllocate]);
+ pResult = holder;
+ // Zero out the mapCodePageCached field (an int value, and it's used to check if the section is initialized or not.)
+ BYTE* pByte = (BYTE*)pResult;
+ FillMemory(pByte + inBytesToAllocate - sizeof(int), sizeof(int), 0);
+ holder.SuppressRelease();
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return pResult;
+}
+FCIMPLEND
+#endif // FEATURE_CODEPAGES_FILE
+
+// InternalIsSortable
+//
+// Called by CompareInfo.IsSortable() to determine if a string has entirely sortable (ie: defined) code points.
+BOOL QCALLTYPE COMNlsInfo::InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(CheckPointer(string));
+ } CONTRACTL_END;
+ BOOL result = FALSE;
+ BEGIN_QCALL;
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
+ result = SortVersioning::SortDllIsDefinedString((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, 0, string, length);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ result = (curDomain->m_pCustomSortLibrary->pIsNLSDefinedString)(COMPARE_STRING, 0, NULL, string, length);
+ }
+ else
+#endif
+ {
+ // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now
+ result = NewApis::IsNLSDefinedString(COMPARE_STRING, 0, NULL, string, length);
+ }
+
+ END_QCALL;
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// InternalGetUserDefaultLocaleName
+//
+// Returns a string with the name of our LCID and returns 0 in LCID.
+// If we cant return
+//
+////////////////////////////////////////////////////////////////////////////
+// This is new to longhorn
+BOOL QCALLTYPE COMNlsInfo::InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION((langType == LOCALE_SYSTEM_DEFAULT) || (langType == LOCALE_USER_DEFAULT));
+ } CONTRACTL_END;
+
+ BOOL result;
+ BEGIN_QCALL;
+
+ WCHAR strName[LOCALE_NAME_MAX_LENGTH];
+ int size = 0;
+
+ if (langType == LOCALE_SYSTEM_DEFAULT)
+ {
+ size = NewApis::GetSystemDefaultLocaleName(strName,NumItems(strName));
+ }
+ else
+ {
+ _ASSERT(langType == LOCALE_USER_DEFAULT);
+ size = NewApis::GetUserDefaultLocaleName(strName,NumItems(strName));
+ }
+
+ // Not found, either not longhorn (no LOCALE_SNAME) or not a valid name
+ if (size == 0)
+ {
+ result = false;
+ }
+ else
+ {
+ defaultLocaleName.Set(strName);
+ result = true;
+ }
+ END_QCALL;
+ return result;
+}
+
+BOOL QCALLTYPE COMNlsInfo::InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage)
+{
+ QCALL_CONTRACT;
+ BOOL result;
+ BEGIN_QCALL;
+
+ WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
+
+ int systemDefaultUiLcid = GetSystemDefaultUILanguage();
+ if(systemDefaultUiLcid == LANGID_ZH_TW)
+ {
+ if (!NewApis::IsZhTwSku())
+ {
+ systemDefaultUiLcid = LANGID_ZH_HK;
+ }
+ }
+
+ int length = NewApis::LCIDToLocaleName(systemDefaultUiLcid, localeName, NumItems(localeName), 0);
+ if (length == 0)
+ {
+ result = false;
+ }
+ else
+ {
+ systemDefaultUiLanguage.Set(localeName);
+ result = true;
+ }
+
+ END_QCALL;
+ return result;
+}
+
+/*
+ */
+BOOL QCALLTYPE COMNlsInfo::InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage)
+{
+ QCALL_CONTRACT;
+ BOOL result;
+ BEGIN_QCALL;
+
+ WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH];
+ LPCWSTR wszLangName=NULL;
+
+ int res= 0;
+ ULONG uLangCount=0;
+ ULONG uBufLen=0;
+ res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,NULL,&uBufLen);
+ if (res == 0)
+ ThrowLastError();
+
+
+ NewArrayHolder<WCHAR> sPreferredLanguages(NULL);
+
+ if (uBufLen > 0 && uLangCount > 0 )
+ {
+ sPreferredLanguages = new WCHAR[uBufLen];
+ res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,sPreferredLanguages,&uBufLen);
+
+ if (res == 0)
+ ThrowLastError();
+
+ wszLangName=sPreferredLanguages;
+// Review size_t to int conversion (possible loss of data).
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4267)
+#endif
+ res=wcslen(wszLangName)+1;
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ }
+ else
+ {
+ res=0;
+ }
+
+ if (res == 0) {
+ res = NewApis::GetUserDefaultLocaleName(wszBuffer,NumItems(wszBuffer));
+ wszLangName=wszBuffer;
+ }
+
+
+ // If not found, either not longhorn (no LOCALE_SNAME) or not a valid name
+ if (res == 0)
+ {
+ // Didn't find string, return an empty string.
+ result = false;
+ }
+ else
+ {
+ userDefaultUiLanguage.Set(wszLangName);
+ result = true;
+ }
+
+ // Return the found language name. LCID should be found one already.
+ END_QCALL;
+ return result;
+}
+
+// Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
+#ifdef FEATURE_CORECLR
+FCIMPL0(Object*, COMNlsInfo::nativeGetResourceFallbackArray)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ } CONTRACTL_END;
+
+ DWORD dwFlags = MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
+ ULONG cchLanguagesBuffer = 0;
+ ULONG ulNumLanguages = 0;
+ BOOL result = FALSE;
+
+ struct _gc
+ {
+ PTRARRAYREF resourceFallbackArray;
+ } gc;
+
+ gc.resourceFallbackArray = NULL;
+
+ // If the resource lookups we're planning on doing are going to be written to a non-Unicode console,
+ // then we should ideally only return languages that can be displayed correctly on the console. The
+ // trick is guessing at whether we're writing this data to the console, which we can't do well.
+ // Instead, we ask new apps to call GetConsoleFallbackUICulture & fall back to en-US.
+ bool disableUserFallback;
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
+ disableUserFallback = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Resources_DisableUserPreferredFallback) == 1
+ END_SO_INTOLERANT_CODE;
+
+ if (disableUserFallback)
+ return NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ // first call with null buffer to get size
+ result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, NULL, &cchLanguagesBuffer);
+ if (cchLanguagesBuffer > 0)
+ {
+ NewArrayHolder<WCHAR> stringBuffer = new (nothrow) WCHAR[cchLanguagesBuffer];
+ if (stringBuffer != NULL)
+ {
+ result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, stringBuffer, &cchLanguagesBuffer);
+ _ASSERTE(result);
+
+ // now string into strings
+ gc.resourceFallbackArray = (PTRARRAYREF) AllocateObjectArray(ulNumLanguages, g_pStringClass);
+
+ LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
+ for(DWORD i = 0; i < ulNumLanguages; i++)
+ {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
+ gc.resourceFallbackArray->SetAt(i, o);
+ buffer += (lstrlenW(buffer) + 1);
+ }
+ }
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.resourceFallbackArray);
+
+}
+FCIMPLEND
+#endif // FEATURE_CORECLR
+
+INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ static INT32 s_lcid = 0;
+
+ // The user UI language cannot change within a process (in fact it cannot change within a logon session),
+ // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are
+ // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call
+ // GetUserDefaultUILanguage needlessly, but the final result is going to be the same.
+ if (s_lcid == 0)
+ {
+ INT32 s_lcidTemp = GetUserDefaultUILanguage();
+ if (s_lcidTemp == LANGID_ZH_TW)
+ {
+ // If the UI language ID is 0x0404, we need to do extra check to decide
+ // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID.
+ if (!NewApis::IsZhTwSku())
+ {
+ s_lcidTemp = LANGID_ZH_HK;
+ }
+ }
+ s_lcid = s_lcidTemp;
+ }
+
+ return s_lcid;
+}
+
+INT_PTR COMNlsInfo::EnsureValidSortHandle(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ if(!(curDomain->m_bUseOsSorting) && handleOrigin == (INT_PTR) SortVersioning::GetSortHandle && ((SortVersioning::PSORTHANDLE) handle)->dwNLSVersion == curDomain->m_sortVersion)
+ {
+ return handle;
+ }
+
+ if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary == NULL && handleOrigin == (INT_PTR) NewApis::LCMapStringEx)
+ {
+ return handle;
+ }
+
+ if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary != NULL && handleOrigin == (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx)
+ {
+ return handle;
+ }
+
+ // At this point, we can't reuse the sort handle (it has different sort semantics than this domain) so we need to get a new one.
+ INT_PTR newHandleOrigin;
+ return InitSortHandleHelper(localeName, &newHandleOrigin);
+#else
+ // For CoreCLR, on Windows 8 and up the handle will be valid. on downlevels the handle will be null
+ return handle;
+#endif
+}
+
+#ifdef FEATURE_SYNTHETIC_CULTURES
+////////////////////////////////////////////////////////////////////////////
+//
+// WstrToInteger4
+//
+////////////////////////////////////////////////////////////////////////////
+
+/*=================================WstrToInteger4==================================
+**Action: Convert a Unicode string to an integer. Error checking is ignored.
+**Returns: The integer value of wstr
+**Arguments:
+** wstr: NULL terminated wide string. Can have character 0'-'9', 'a'-'f', and 'A' - 'F'
+** Radix: radix to be used in the conversion.
+**Exceptions: None.
+==============================================================================*/
+
+INT32 COMNlsInfo::WstrToInteger4(
+ __in_z LPCWSTR wstr,
+ __in int Radix)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(wstr));
+ PRECONDITION(Radix > 1 && Radix <= 16);
+ } CONTRACTL_END;
+ INT32 Value = 0;
+ int Base = 1;
+
+ for (int Length = Wszlstrlen(wstr) - 1; Length >= 0; Length--)
+
+ {
+ WCHAR ch = wstr[Length];
+ _ASSERTE((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'));
+ if (ch >= 'a')
+ {
+ ch = ch - 'a' + 'A';
+ }
+
+ Value += ((ch >= 'A') ? (ch - 'A' + 10) : (ch - '0')) * Base;
+ Base *= Radix;
+ }
+
+ return (Value);
+}
+#endif // FEATURE_SYNTHETIC_CULTURES
+
+
+#ifndef FEATURE_CORECLR
+FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeSetThreadLocale, StringObject* localeNameUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ LCID lcid = 0;
+
+ // TODO: NLS Arrowhead -A bit scary becausue Set ThreadLocale can't handle custom cultures?
+ STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
+ lcid=NewApis::LocaleNameToLCID(localeName->GetBuffer(),0);
+ if (lcid == 0)
+ {
+ ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
+ }
+ HELPER_METHOD_FRAME_END();
+
+
+ BOOL result = TRUE;
+
+ // SetThreadLocale doesn't handle names/custom cultures
+#ifdef _MSC_VER
+// Get rid of the SetThreadLocale warning in OACR:
+#pragma warning(push)
+#pragma warning(disable:38010)
+#endif
+ result = ::SetThreadLocale(lcid);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ FC_RETURN_BOOL(result);
+}
+FCIMPLEND
+#endif
+
+
+FCIMPL2(Object*, COMNlsInfo::nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ STRINGREF refRetVal;
+ } gc;
+
+ // Dereference our string
+ gc.refRetVal = NULL;
+ gc.localeName = (STRINGREF)localeNameUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
+
+ WCHAR buffer[LOCALE_MAX_STRING_SIZE];
+ int result = NewApis::GetLocaleInfoEx(localeNameStackBuffer, lcType, buffer, NumItems(buffer));
+
+ // Make a string out of it
+ if (result != 0)
+ {
+ // Exclude the NULL char at the end, except that LOCALE_FONTSIGNATURE isn't
+ // really a string, so we need the last character too.
+ gc.refRetVal = StringObject::NewString(buffer, ((lcType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_FONTSIGNATURE) ? result : result-1);
+ }
+ else
+ {
+ }
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(gc.refRetVal);
+}
+FCIMPLEND
+
+
+FCIMPL2(INT32, COMNlsInfo::nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ INT32 result = 0;
+
+ // Dereference our string
+ STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
+
+ lcType |= LOCALE_RETURN_NUMBER;
+
+ if (NewApis::GetLocaleInfoEx(localeName->GetBuffer(), lcType, (LPWSTR)&result, sizeof(INT32) / sizeof (WCHAR)) == 0)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::nativeGetLocaleInfoExInt! This could be a CultureInfo bug (bad localeName string) or maybe a GCHole.");
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return result;
+}
+FCIMPLEND
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Call the Win32 GetLocaleInfo() using the specified lcid to retrieve
+// the native digits, probably from the registry override. The return
+// indicates whether the call was successful.
+//
+// Parameters:
+// IN lcid the LCID to make the Win32 call with
+// OUT pOutputStrAry The output managed string array.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL COMNlsInfo::GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF * pOutputStrAry, BOOL useUserOverride)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ THROWS;
+ } CONTRACTL_END;
+
+ WCHAR buffer[11];
+ int result = 0;
+
+ DWORD lcType = LOCALE_SNATIVEDIGITS;
+ if(!useUserOverride)
+ {
+ lcType |= LOCALE_NOUSEROVERRIDE;
+ }
+ result = NewApis::GetLocaleInfoEx(locale, lcType, buffer, 11);
+ // Be very unforgiving and only support strings of size 10 plus the NULL
+ if (result == 11)
+ {
+ // Break up the unmanaged ten-character ZLS into what NFI wants (a managed
+ // ten-string array).
+ //
+ // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
+ // an OutOfMemoryException if there's not enough memory.
+ //
+ PTRARRAYREF DigitArray = (PTRARRAYREF) AllocateObjectArray(10, g_pStringClass);
+
+ GCPROTECT_BEGIN(DigitArray);
+ for(DWORD i = 0; i < 10; i++) {
+ OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer + i, 1);
+ DigitArray->SetAt(i, o);
+ }
+ GCPROTECT_END();
+
+ _ASSERTE(pOutputStrAry != NULL);
+ *pOutputStrAry = DigitArray;
+ }
+
+ return (result == 11);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Call the Win32 GetLocaleInfoEx() using the specified lcid and LCTYPE.
+// The return value can be INT32 or an allocated managed string object, depending on
+// which version's called.
+//
+// Parameters:
+// OUT pOutputInt32 The output int32 value.
+// OUT pOutputRef The output string value.
+//
+////////////////////////////////////////////////////////////////////////
+BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, INT32* pOutputInt32, BOOL useUserOverride)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ MODE_ANY;
+ NOTHROW;
+ } CONTRACTL_END;
+
+ int result = 0;
+
+ _ASSERT((lcType & LOCALE_RETURN_NUMBER) != 0);
+ if(!useUserOverride)
+ {
+ lcType |= LOCALE_NOUSEROVERRIDE;
+ }
+ result = NewApis::GetLocaleInfoEx(localeName, lcType, (LPWSTR)pOutputInt32, sizeof(*pOutputInt32));
+
+ return (result != 0);
+}
+
+BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride)
+{
+ CONTRACTL
+ {
+ THROWS; // We can throw since we are allocating managed string.
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
+ int result = 0;
+
+ _ASSERT((lcType & LOCALE_RETURN_NUMBER) == 0);
+ if(!useUserOverride)
+ {
+ lcType |= LOCALE_NOUSEROVERRIDE;
+ }
+ result = NewApis::GetLocaleInfoEx(localeName, lcType, buffer, LOCALE_NAME_MAX_LENGTH);
+
+ if (result != 0)
+ {
+ _ASSERTE(pOutputStrRef != NULL);
+ *pOutputStrRef = StringObject::NewString(buffer, result - 1);
+ }
+
+ return (result != 0);
+}
+
+FCIMPL1(Object*, COMNlsInfo::LCIDToLocaleName, LCID lcid)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+
+ // The maximum size for locale name is 85 characters.
+ WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
+ int result = 0;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ // Note that this'll return neutral names (unlike native Vista APIs)
+ result = NewApis::LCIDToLocaleName(lcid, localeName, LOCALE_NAME_MAX_LENGTH, 0);
+
+ if (result != 0)
+ {
+ refRetVal = StringObject::NewString(localeName, result - 1);
+ }
+ else
+ {
+ refRetVal = StringObject::GetEmptyString();
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL1(INT32, COMNlsInfo::LocaleNameToLCID, StringObject* localeNameUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ INT32 result = 0;
+
+ // Dereference our string
+ STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
+
+ // Note that this'll return neutral names (unlike native Vista APIs)
+ result = NewApis::LocaleNameToLCID(localeName->GetBuffer(), 0);
+
+ HELPER_METHOD_FRAME_END();
+
+ return result;
+}
+FCIMPLEND
+
+////////////////////////////////////////////////////////////////////////
+//
+// Implementation of CultureInfo.nativeGetNumberFormatInfoValues.
+//
+// Retrieve NumberFormatInfo (NFI) properties from windows
+//
+// Parameters:
+// IN/OUT pNumfmtUNSAFE
+// The pointer of the managed NumberFormatInfo passed
+// from the managed side.
+// Note that the native NumberFormatInfo* is defined
+// in COMNumber.h
+// Note:
+// Managed string will be allocated and assign to the string fields in
+// the managed NumberFormatInfo passed in pNumftUNSAFE
+//
+////////////////////////////////////////////////////////////////////////
+
+
+/*
+ This is the list of the data members in the managed NumberFormatInfo and their
+ corresponding LCTYPE().
+
+ Win32 GetLocaleInfo() constants Data members in NumberFormatInfo in the defined order.
+ LOCALE_SPOSITIVE // String positiveSign
+ LOCALE_SNEGATIVE // String negativeSign
+ LOCALE_SDECIMAL // String numberDecimalSeparator
+ LOCALE_SGROUPING // String numberGroupSeparator
+ LOCALE_SMONGROUPING // String currencyGroupSeparator
+ LOCALE_SMONDECIMALSEP // String currencyDecimalSeparator
+ LOCALE_SCURRENCY // String currencySymbol
+ N/A // String ansiCurrencySymbol
+ N/A // String nanSymbol
+ N/A // String positiveInfinitySymbol
+ N/A // String negativeInfinitySymbol
+ N/A // String percentDecimalSeparator
+ N/A // String percentGroupSeparator
+ N/A // String percentSymbol
+ N/A // String perMilleSymbol
+
+ N/A // int m_dataItem
+
+ LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, // int numberDecimalDigits
+ LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // int currencyDecimalDigits
+ LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER, // int currencyPositivePattern
+ LOCALE_INEGCURR | LOCALE_RETURN_NUMBER, // int currencyNegativePattern
+ LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER, // int numberNegativePattern
+ N/A // int percentPositivePattern
+ N/A // int percentNegativePattern
+ N/A // int percentDecimalDigits
+ N/A // bool isReadOnly=false;
+ N/A // internal bool m_useUserOverride;
+*/
+FCIMPL3(FC_BOOL_RET, COMNlsInfo::nativeGetNumberFormatInfoValues,
+ StringObject* localeNameUNSAFE, NumberFormatInfo* pNumfmtUNSAFE, CLR_BOOL useUserOverride) {
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ BOOL ret = TRUE;
+
+ struct _gc
+ {
+ STRINGREF localeName;
+ STRINGREF stringResult;
+ NUMFMTREF numfmt;
+ PTRARRAYREF tempArray;
+ } gc;
+
+ // Dereference our string
+ gc.localeName = (STRINGREF)localeNameUNSAFE;
+ gc.numfmt = (NUMFMTREF) pNumfmtUNSAFE;
+ gc.stringResult = NULL;
+ gc.tempArray = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
+
+ // Calling SString::ConvertToUnicode once
+ LPCWSTR pLocaleName = localeNameStackBuffer;
+
+ //
+ // NOTE: We pass the stringResult allocated in the stack and assign it to the fields
+ // in numfmt after calling CallGetLocaleInfo(). The reason for this is that CallGetLocaleInfo()
+ // allocates a string object, and it may trigger a GC and cause numfmt to be moved.
+ // That's why we use the stringResult allocated in the stack since it will not be moved.
+ // After CallGetLocaleInfo(), we know that numfmt will not be moved, and it's safe to assign
+ // the stringResult to its field.
+ //
+
+ // String values
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SPOSITIVESIGN , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sPositive), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SNEGATIVESIGN , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sNegative), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SDECIMAL , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberDecimal), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_STHOUSAND , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberGroup), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONTHOUSANDSEP , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyGroup), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONDECIMALSEP , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyDecimal), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+ if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SCURRENCY , &gc.stringResult, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrency), gc.stringResult, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+
+
+ // Numeric values
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNumberDecimals), useUserOverride);
+ _ASSERT(ret == TRUE);
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cCurrencyDecimals), useUserOverride);
+ _ASSERT(ret == TRUE);
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER , &(gc.numfmt->cPosCurrencyFormat), useUserOverride);
+ _ASSERT(ret == TRUE);
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegCurrencyFormat), useUserOverride);
+ _ASSERT(ret == TRUE);
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegativeNumberFormat), useUserOverride);
+ _ASSERT(ret == TRUE);
+ ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, &(gc.numfmt->iDigitSubstitution), useUserOverride);
+ _ASSERT(ret == TRUE);
+
+ // LOCALE_SNATIVEDIGITS (gc.tempArray of strings)
+ if (GetNativeDigitsFromWin32(pLocaleName, &gc.tempArray, useUserOverride)) {
+ SetObjectReference((OBJECTREF*)&(gc.numfmt->sNativeDigits), gc.tempArray, NULL);
+ }
+ else {
+ ret = FALSE;
+ _ASSERT(FALSE);
+ }
+
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// Culture enumeration helper functions
+//
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Enum values for System.Globalization.CultureTypes
+//
+////////////////////////////////////////////////////////////////////////////
+
+// Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags
+#define CULTURETYPES_NEUTRALCULTURES 0x0001
+
+// Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags
+#define CULTURETYPES_SPECIFICCULTURES 0x0002
+
+// Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures
+#define CULTURETYPES_INSTALLEDWIN32CULTURES 0x0004
+
+// User defined custom culture
+#define CULTURETYPES_USERCUSTOMCULTURE 0x0008
+
+// User defined replacement custom culture.
+#define CULTURETYPES_REPLACEMENTCULTURES 0x0010
+// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
+// Culture exists in Win32 but not in the Framework. // TODO: All cultures or no cultures?
+#define CULTURETYPES_WINDOWSONLYCULTURES 0x0020
+// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
+// the language tag match a culture that ships with the .NET framework, effectively all cultures since we get them from windows
+#define CULTURETYPES_FRAMEWORKCULTURES 0x0040
+
+
+const LPWSTR WHIDBEY_FRAMEWORK_CULTURE_LIST [] =
+{
+ W(""),
+ W("af"),
+ W("af-za"),
+ W("ar"),
+ W("ar-ae"),
+ W("ar-bh"),
+ W("ar-dz"),
+ W("ar-eg"),
+ W("ar-iq"),
+ W("ar-jo"),
+ W("ar-kw"),
+ W("ar-lb"),
+ W("ar-ly"),
+ W("ar-ma"),
+ W("ar-om"),
+ W("ar-qa"),
+ W("ar-sa"),
+ W("ar-sy"),
+ W("ar-tn"),
+ W("ar-ye"),
+ W("az"),
+ W("az-cyrl-az"),
+ W("az-latn-az"),
+ W("be"),
+ W("be-by"),
+ W("bg"),
+ W("bg-bg"),
+ W("ca"),
+ W("ca-es"),
+ W("cs"),
+ W("cs-cz"),
+ W("da"),
+ W("da-dk"),
+ W("de"),
+ W("de-at"),
+ W("de-ch"),
+ W("de-de"),
+ W("de-li"),
+ W("de-lu"),
+ W("dv"),
+ W("dv-mv"),
+ W("el"),
+ W("el-gr"),
+ W("en"),
+ W("en-029"),
+ W("en-au"),
+ W("en-bz"),
+ W("en-ca"),
+ W("en-gb"),
+ W("en-ie"),
+ W("en-jm"),
+ W("en-nz"),
+ W("en-ph"),
+ W("en-tt"),
+ W("en-us"),
+ W("en-za"),
+ W("en-zw"),
+ W("es"),
+ W("es-ar"),
+ W("es-bo"),
+ W("es-cl"),
+ W("es-co"),
+ W("es-cr"),
+ W("es-do"),
+ W("es-ec"),
+ W("es-es"),
+ W("es-gt"),
+ W("es-hn"),
+ W("es-mx"),
+ W("es-ni"),
+ W("es-pa"),
+ W("es-pe"),
+ W("es-pr"),
+ W("es-py"),
+ W("es-sv"),
+ W("es-uy"),
+ W("es-ve"),
+ W("et"),
+ W("et-ee"),
+ W("eu"),
+ W("eu-es"),
+ W("fa"),
+ W("fa-ir"),
+ W("fi"),
+ W("fi-fi"),
+ W("fo"),
+ W("fo-fo"),
+ W("fr"),
+ W("fr-be"),
+ W("fr-ca"),
+ W("fr-ch"),
+ W("fr-fr"),
+ W("fr-lu"),
+ W("fr-mc"),
+ W("gl"),
+ W("gl-es"),
+ W("gu"),
+ W("gu-in"),
+ W("he"),
+ W("he-il"),
+ W("hi"),
+ W("hi-in"),
+ W("hr"),
+ W("hr-hr"),
+ W("hu"),
+ W("hu-hu"),
+ W("hy"),
+ W("hy-am"),
+ W("id"),
+ W("id-id"),
+ W("is"),
+ W("is-is"),
+ W("it"),
+ W("it-ch"),
+ W("it-it"),
+ W("ja"),
+ W("ja-jp"),
+ W("ka"),
+ W("ka-ge"),
+ W("kk"),
+ W("kk-kz"),
+ W("kn"),
+ W("kn-in"),
+ W("ko"),
+ W("ko-kr"),
+ W("kok"),
+ W("kok-in"),
+ W("ky"),
+ W("ky-kg"),
+ W("lt"),
+ W("lt-lt"),
+ W("lv"),
+ W("lv-lv"),
+ W("mk"),
+ W("mk-mk"),
+ W("mn"),
+ W("mn-mn"),
+ W("mr"),
+ W("mr-in"),
+ W("ms"),
+ W("ms-bn"),
+ W("ms-my"),
+ W("nb-no"),
+ W("nl"),
+ W("nl-be"),
+ W("nl-nl"),
+ W("nn-no"),
+ W("no"),
+ W("pa"),
+ W("pa-in"),
+ W("pl"),
+ W("pl-pl"),
+ W("pt"),
+ W("pt-br"),
+ W("pt-pt"),
+ W("ro"),
+ W("ro-ro"),
+ W("ru"),
+ W("ru-ru"),
+ W("sa"),
+ W("sa-in"),
+ W("sk"),
+ W("sk-sk"),
+ W("sl"),
+ W("sl-si"),
+ W("sq"),
+ W("sq-al"),
+ W("sr"),
+ W("sr-cyrl-cs"),
+ W("sr-latn-cs"),
+ W("sv"),
+ W("sv-fi"),
+ W("sv-se"),
+ W("sw"),
+ W("sw-ke"),
+ W("syr"),
+ W("syr-sy"),
+ W("ta"),
+ W("ta-in"),
+ W("te"),
+ W("te-in"),
+ W("th"),
+ W("th-th"),
+ W("tr"),
+ W("tr-tr"),
+ W("tt"),
+ W("tt-ru"),
+ W("uk"),
+ W("uk-ua"),
+ W("ur"),
+ W("ur-pk"),
+ W("uz"),
+ W("uz-cyrl-uz"),
+ W("uz-latn-uz"),
+ W("vi"),
+ W("vi-vn"),
+ W("zh-chs"),
+ W("zh-cht"),
+ W("zh-cn"),
+ W("zh-hans"),
+ W("zh-hant"),
+ W("zh-hk"),
+ W("zh-mo"),
+ W("zh-sg"),
+ W("zh-tw")
+};
+#define WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH (sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST) / sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST[0]))
+
+////////////////////////////////////////////////////////////////////////////
+//
+// NlsCompareInvariantNoCase
+//
+// This routine does fast caseless comparison without needing the tables.
+// This helps us do the comparisons we need to load the tables :-)
+//
+// Returns 0 if identical, <0 if pFirst if first string sorts first.
+//
+// This is only intended to help with our locale name comparisons,
+// which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
+// compare as equal.
+//
+// WARNING: [\]^_` will be less than A-Z because we make everything lower
+// case before comparing them.
+//
+// When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
+// When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
+// or when null terminators are reached, whichever happens first (strncmp-like behavior)
+//
+////////////////////////////////////////////////////////////////////////////
+
+int NlsCompareInvariantNoCase(
+ LPCWSTR pFirst,
+ LPCWSTR pSecond,
+ int size,
+ BOOL bNullEnd)
+{
+ int i=0;
+ WCHAR first;
+ WCHAR second;
+
+ for (;
+ size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
+ size--, pFirst++, pSecond++)
+ {
+ // Make them lower case
+ if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
+ if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
+
+ // Get the diff
+ i = (first - second);
+
+ // Are they the same?
+ if (i == 0)
+ continue;
+
+ // Otherwise the difference. Remember we made A-Z into lower case, so
+ // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
+ // characters between A-Z and a-Z in ascii)
+ return i;
+ }
+
+ // When we are here, one of these holds:
+ // size == 0
+ // or one of the strings has a null terminator
+ // or both of the string reaches null terminator
+
+ if (bNullEnd || size != 0)
+ {
+ // If bNullEnd is TRUE, always check for null terminator.
+ // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
+ // than another (hense the size != 0 check).
+
+ // See if one string ended first
+ if (*pFirst != 0 || *pSecond != 0)
+ {
+ // Which one?
+ return *pFirst == 0 ? -1 : 1;
+ }
+ }
+
+ // Return our difference (0)
+ return i;
+}
+
+BOOL IsWhidbeyFrameworkCulture(__in LPCWSTR lpLocaleString)
+{
+ int iBottom = 0;
+ int iTop = WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH - 1;
+
+ // Do a binary search for our name
+ while (iBottom <= iTop)
+ {
+ int iMiddle = (iBottom + iTop) / 2;
+ int result = NlsCompareInvariantNoCase(lpLocaleString, WHIDBEY_FRAMEWORK_CULTURE_LIST[iMiddle], LOCALE_NAME_MAX_LENGTH, TRUE);
+ if (result == 0)
+ {
+ return TRUE;
+ }
+ if (result < 0)
+ {
+ // pLocaleName was < pTest
+ iTop = iMiddle - 1;
+ }
+ else
+ {
+ // pLocaleName was > pTest
+ iBottom = iMiddle + 1;
+ }
+ }
+
+ return FALSE;
+}
+
+// Just Check to see if the OS thinks it is a valid locle
+BOOL WINAPI IsOSValidLocaleName(__in LPCWSTR lpLocaleName, bool bIsNeutralLocale)
+{
+#ifndef ENABLE_DOWNLEVEL_FOR_NLS
+ return ::IsValidLocaleName(lpLocaleName);
+#else
+ BOOL IsWindows7 = NewApis::IsWindows7Platform();
+ // if we're < Win7, we didn't know about neutrals or the invariant.
+ if (!IsWindows7 && (bIsNeutralLocale || (lpLocaleName[0] == 0)))
+ {
+ return false;
+ }
+
+ // Work around the name/lcid thingy (can't just link to ::IsValidLocaleName())
+ LCID lcid = NewApis::LocaleNameToLCID(lpLocaleName, 0);
+
+ if (IsCustomCultureId(lcid))
+ {
+ return false;
+ }
+
+ if (bIsNeutralLocale)
+ {
+ // In this case, we're running on Windows 7.
+ // For neutral locales, use GetLocaleInfoW.
+ // If GetLocaleInfoW works, then the OS knows about it.
+ return (::GetLocaleInfoW(lcid, LOCALE_ILANGUAGE, NULL, 0) != 0);
+ }
+
+ // This is not a custom locale.
+ // Call IsValidLocale() to check if the LCID is installed.
+ // IsValidLocale doesn't work for neutral locales.
+ return IsValidLocale(lcid, LCID_INSTALLED);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Check the dwFlags, which has the 'attributes' of the locale, and decide
+// if the locale should be included in the enumeration based on
+// the desired CultureTypes.
+//
+////////////////////////////////////////////////////////////////////////////
+
+BOOL ShouldIncludeByCultureType(INT32 cultureTypes, LPCWSTR lpLocaleString, INT32 dwFlags)
+{
+
+ if ((cultureTypes & CULTURETYPES_NEUTRALCULTURES) &&
+ ((dwFlags & LOCALE_NEUTRALDATA) || (lpLocaleString[0] == 0))) // Invariant culture get enumerated with the neutrals
+ {
+ return TRUE;
+ }
+
+ if ((cultureTypes & CULTURETYPES_SPECIFICCULTURES) &&
+ ((dwFlags & LOCALE_SPECIFICDATA) && (lpLocaleString[0] != 0))) // Invariant culture does not get enumerated with the specifics
+ {
+ return TRUE;
+ }
+
+ if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
+ {
+ // The user asks for installed Win32 culture, so check
+ // if this locale is installed. In W7 and above, when ::IsValidLocaleName()
+ // returns true, it means that it is installed.
+ // In downlevel (including Vista), we will convert the name to LCID.
+ // When the LCID is not a custom locale, we will call ::IsValidLocale(.., LCID_INSTALLED)
+ // to verify if the locale is installed.
+ // In Vista, we treat custom locale as installed.
+ if (IsOSValidLocaleName(lpLocaleString, (dwFlags & LOCALE_NEUTRALDATA) == LOCALE_NEUTRALDATA))
+ {
+ return TRUE;
+ }
+ }
+
+ if ((cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) &&
+ (dwFlags & LOCALE_SUPPLEMENTAL))
+ {
+ return TRUE;
+ }
+
+ if ((cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) &&
+ (dwFlags & LOCALE_REPLACEMENT))
+ {
+ return TRUE;
+ }
+
+ if ((cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) &&
+ IsWhidbeyFrameworkCulture(lpLocaleString))
+ {
+ return TRUE;
+ }
+
+ //
+ // No need to check CULTURETYPES_WINDOWSONLYCULTURES and CULTURETYPES_FRAMEWORKCULTURES
+ // since they are deprecated, and they are handled in the managed code before calling
+ // nativeEnumCultureNames.
+ //
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Struct to hold context to be used in the callback for
+// EnumLocaleProcessingCallback
+//
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+ PTRARRAYREF pCultureNamesArray;
+ INT32 count;
+ INT32 cultureTypes;
+} ENUM_LOCALE_DATA;
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Callback for NewApis::EnumSystemLocalesEx to count the number of
+// locales to be enumerated.
+//
+////////////////////////////////////////////////////////////////////////////
+
+BOOL CALLBACK EnumLocaleCountCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
+{
+ ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
+
+ if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
+ {
+ (pData->count)++;
+ }
+ return TRUE;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Callback for NewApis::EnumSystemLocalesEx to add the locale name
+// into the allocated managed string array.
+//
+////////////////////////////////////////////////////////////////////////////
+
+BOOL CALLBACK EnumLocaleProcessingCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
+{
+ ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
+
+ if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
+ {
+ GCX_COOP();
+
+ GCPROTECT_BEGIN(pData->pCultureNamesArray);
+
+ OBJECTREF cultureString = (OBJECTREF) StringObject::NewString(lpLocaleString);
+ pData->pCultureNamesArray->SetAt(pData->count, cultureString);
+ pData->count++;
+
+ GCPROTECT_END();
+ }
+
+ return TRUE;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Called by CultureData.GetCultures() to enumerate the names of cultures.
+// It first calls NewApis::EnumSystemLocalesEx to count the number of
+// locales to be enumerated. And it will allocate an managed string
+// array with the count. And fill the array with the culture names in
+// the 2nd call to NewAPis::EnumSystemLocalesEx.
+//
+////////////////////////////////////////////////////////////////////////////
+
+
+int QCALLTYPE COMNlsInfo::nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ // Check CultureTypes.WindowsOnlyCultures and CultureTYpes.FrameworkCultures are deprecated and is
+ // handled in the managed code side to provide fallback behavior.
+ //PRECONDITION((cultureTypes & (CULTURETYPES_WINDOWSONLYCULTURES | CULTURETYPES_FRAMEWORKCULTURES) == 0));
+ } CONTRACTL_END;
+
+
+ int result;
+ DWORD dwFlags = 0;
+ PTRARRAYREF cultureNamesArray = NULL;
+ ENUM_LOCALE_DATA enumData = { NULL, 0, cultureTypes};
+
+ BEGIN_QCALL;
+
+ //
+ // if CultureTypes.FrameworkCulture is specified we'll enumerate all cultures
+ // and filter according to the Whidbey framework culture list (for compatibility)
+ //
+
+ if (cultureTypes & CULTURETYPES_FRAMEWORKCULTURES)
+ {
+ dwFlags |= LOCALE_NEUTRALDATA | LOCALE_SPECIFICDATA;
+ }
+
+ // Map CultureTypes to Windows enumeration values.
+ if (cultureTypes & CULTURETYPES_NEUTRALCULTURES)
+ {
+ dwFlags |= LOCALE_NEUTRALDATA;
+ }
+
+ if (cultureTypes & CULTURETYPES_SPECIFICCULTURES)
+ {
+ dwFlags |= LOCALE_SPECIFICDATA;
+ }
+
+ if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
+ {
+ // Windows 7 knows about neutrals, whereas Vista and lower don't.
+ if (NewApis::IsWindows7Platform())
+ {
+ dwFlags |= LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA;
+ }
+ else
+ {
+ dwFlags |= LOCALE_SPECIFICDATA;
+ }
+ }
+
+ dwFlags |= (cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) ? LOCALE_SUPPLEMENTAL: 0;
+
+ // We need special handling for Replacement cultures because Windows does not have a way to enumerate it directly.
+ // Replacement locale check will be only used when CultureTypes.SpecificCultures is NOT used.
+ dwFlags |= (cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) ? LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA: 0;
+
+
+ result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleCountCallback, dwFlags, (LPARAM)&enumData, NULL) == TRUE ? 1 : 0;
+
+ if (result)
+ {
+
+ GCX_COOP();
+
+ GCPROTECT_BEGIN(cultureNamesArray);
+
+ // Now we need to allocate our culture names string array and populate it
+ // Get our array object (will throw, don't have to check it)
+ cultureNamesArray = (PTRARRAYREF) AllocateObjectArray(enumData.count, g_pStringClass);
+
+ // In the context struct passed to EnumSystemLocalesEx, reset the count and assign the newly allocated string array
+ // to hold culture names to be enumerated.
+ enumData.count = 0;
+ enumData.pCultureNamesArray = cultureNamesArray;
+
+ result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleProcessingCallback, dwFlags, (LPARAM)&enumData, NULL);
+
+ if (result)
+ {
+ retStringArray.Set(cultureNamesArray);
+ }
+ GCPROTECT_END();
+ }
+ END_QCALL
+
+ return result;
+
+}
+
+//
+// InternalCompareString is used in the managed side to handle the synthetic CompareInfo methods (IndexOf, LastIndexOf, IsPrfix, and IsSuffix)
+//
+INT32 QCALLTYPE COMNlsInfo::InternalCompareString(
+ INT_PTR handle,
+ INT_PTR handleOrigin,
+ LPCWSTR localeName,
+ LPCWSTR string1, INT32 offset1, INT32 length1,
+ LPCWSTR string2, INT32 offset2, INT32 length2,
+ INT32 flags)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(CheckPointer(string1));
+ PRECONDITION(CheckPointer(string2));
+ PRECONDITION(CheckPointer(localeName));
+ } CONTRACTL_END;
+
+ INT32 result = 1;
+ BEGIN_QCALL;
+
+ handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ result = SortVersioning::SortDllCompareString((SortVersioning::PSORTHANDLE) handle, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, 0);
+ }
+ else if (curDomain->m_pCustomSortLibrary != NULL) {
+ result = (curDomain->m_pCustomSortLibrary->pCompareStringEx)(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle);
+ }
+
+ switch (result)
+ {
+ case CSTR_LESS_THAN:
+ result = -1;
+ break;
+
+ case CSTR_EQUAL:
+ result = 0;
+ break;
+
+ case CSTR_GREATER_THAN:
+ result = 1;
+ break;
+
+ case 0:
+ default:
+ _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
+ break;
+ }
+
+ END_QCALL;
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// UseConstantSpaceHashAlgorithm
+// Check for the DWORD "NetFx45_CultureAwareComparerGetHashCode_LongStrings" CLR config option.
+//
+// .Net 4.5 introduces an opt-in algorithm for determining the hash code of strings that
+// uses a constant amount of memory instead of memory proportional to the size of the string
+//
+// A non-zero value will enable the new algorithm:
+//
+// 1) Config file (MyApp.exe.config)
+// <?xml version ="1.0"?>
+// <configuration>
+// <runtime>
+// <NetFx45_CultureAwareComparerGetHashCode_LongStrings enabled="1"/>
+// </runtime>
+// </configuration>
+// 2) Environment variable
+// set NetFx45_CultureAwareComparerGetHashCode_LongStrings=1
+// 3) RegistryKey
+// [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
+// "NetFx45_CultureAwareComparerGetHashCode_LongStrings"=dword:00000001
+//
+////////////////////////////////////////////////////////////////////////////
+BOOL UseConstantSpaceHashAlgorithm()
+{
+ static bool configChecked = false;
+ static BOOL useConstantSpaceHashAlgorithm = FALSE;
+
+ if(!configChecked)
+ {
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
+ useConstantSpaceHashAlgorithm = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_CultureAwareComparerGetHashCode_LongStrings) != 0;
+ END_SO_INTOLERANT_CODE;
+
+ configChecked = true;
+ }
+ return useConstantSpaceHashAlgorithm;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////
+//
+// InternalGetGlobalizedHashCode
+//
+////////////////////////////////////////////////////////////////////////////
+INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, BOOL bForceRandomizedHashing, INT64 additionalEntropy)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(CheckPointer(localeName));
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ } CONTRACTL_END;
+
+ INT32 iReturnHash = 0;
+ BEGIN_QCALL;
+
+ handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
+ int byteCount = 0;
+
+ //
+ // Make sure there is a string.
+ //
+ if (!string) {
+ COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String"));
+ }
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+#endif // FEATURE_CORECLR
+
+ if(length > 0 && UseConstantSpaceHashAlgorithm()
+ // Note that we can't simply do the hash without the entropy and then try to add it after the fact we need the hash function itself to pass entropy to its inputs.
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+ && !bForceRandomizedHashing
+#ifndef FEATURE_CORECLR
+ && !curDomain->m_pNlsHashProvider->GetUseRandomHashing()
+#else
+ && !COMNlsHashProvider::s_NlsHashProvider.GetUseRandomHashing()
+#endif // FEATURE_CORECLR
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ )
+ {
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ iReturnHash=SortVersioning::SortDllGetHashCode((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0);
+ }
+ else
+#endif
+ {
+ int iRes = 0;
+ int iHashValue = 0;
+
+#ifndef FEATURE_CORECLR
+ if (curDomain->m_pCustomSortLibrary != NULL)
+ {
+ iRes = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
+ }
+ else
+#endif
+ {
+ iRes = NewApis::LCMapStringEx(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
+ }
+
+ if(iRes != 0)
+ {
+ iReturnHash = iHashValue;
+ }
+ }
+ }
+
+ if(iReturnHash == 0)
+ {
+ DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn);
+
+ //
+ // Caller has already verified that the string is not of zero length
+ //
+ // Assert if we might hit an AV in LCMapStringEx for the invariant culture.
+ _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0);
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ byteCount=SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0, NULL, 0);
+ }
+ else if (curDomain->m_pCustomSortLibrary != NULL)
+ {
+ byteCount = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
+ }
+
+ //A count of 0 indicates that we either had an error or had a zero length string originally.
+ if (byteCount==0) {
+ COMPlusThrow(kArgumentException, W("Arg_MustBeString"));
+ }
+
+ // We used to use a NewArrayHolder here, but it turns out that hurts our large # process
+ // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack
+ // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case
+ {
+ CQuickBytesSpecifySize<MAX_STRING_VALUE * sizeof(WCHAR)> qbBuffer;
+ BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount);
+
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, pByte, byteCount, NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
+ }
+
+#ifndef FEATURE_CORECLR
+ iReturnHash = curDomain->m_pNlsHashProvider->HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
+#else
+ iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
+#endif // FEATURE_CORECLR
+ }
+ }
+ END_QCALL;
+ return(iReturnHash);
+}
+
+#ifndef FEATURE_CORECLR // FCalls used by System.TimeZone
+
+FCIMPL0(LONG, COMNlsInfo::nativeGetTimeZoneMinuteOffset)
+{
+ FCALL_CONTRACT;
+
+ TIME_ZONE_INFORMATION timeZoneInfo;
+
+ GetTimeZoneInformation(&timeZoneInfo);
+
+ //
+ // In Win32, UTC = local + offset. So for Pacific Standard Time, offset = 8.
+ // In NLS+, Local time = UTC + offset. So for PST, offset = -8.
+ // So we have to reverse the sign here.
+ //
+ return (timeZoneInfo.Bias * -1);
+}
+FCIMPLEND
+
+FCIMPL0(Object*, COMNlsInfo::nativeGetStandardName)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+
+ TIME_ZONE_INFORMATION timeZoneInfo;
+ GetTimeZoneInformation(&timeZoneInfo);
+
+ refRetVal = StringObject::NewString(timeZoneInfo.StandardName);
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL0(Object*, COMNlsInfo::nativeGetDaylightName)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refRetVal = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
+
+ TIME_ZONE_INFORMATION timeZoneInfo;
+ GetTimeZoneInformation(&timeZoneInfo);
+ // Instead of returning null when daylight saving is not used, now we return the same result as the OS.
+ //In this case, if daylight saving time is used, the standard name is returned.
+
+#if 0
+ if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightDate.wMonth == 0) {
+ // If daylight saving time is not used in this timezone, return null.
+ //
+ // Windows NT/2000: TIME_ZONE_ID_UNKNOWN is returned if daylight saving time is not used in
+ // the current time zone, because there are no transition dates.
+ //
+ // For Windows 9x, a zero in the wMonth in DaylightDate means daylight saving time
+ // is not specified.
+ //
+ // If the current timezone uses daylight saving rule, but user unchekced the
+ // "Automatically adjust clock for daylight saving changes", the value
+ // for DaylightBias will be 0.
+ return (I2ARRAYREF)NULL;
+ }
+#endif // 0
+
+ refRetVal = StringObject::NewString(timeZoneInfo.DaylightName);
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(refRetVal);
+}
+FCIMPLEND
+
+FCIMPL1(Object*, COMNlsInfo::nativeGetDaylightChanges, int year)
+{
+ FCALL_CONTRACT;
+
+ I2ARRAYREF pResultArray = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(pResultArray);
+
+ TIME_ZONE_INFORMATION timeZoneInfo;
+ DWORD result = GetTimeZoneInformation(&timeZoneInfo);
+
+ if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightBias == 0
+ || timeZoneInfo.DaylightDate.wMonth == 0
+ ) {
+ // If daylight saving time is not used in this timezone, return null.
+ //
+ // If the current timezone uses daylight saving rule, but user unchekced the
+ // "Automatically adjust clock for daylight saving changes", the value
+ // for DaylightBias will be 0.
+ goto lExit;
+ }
+
+ pResultArray = (I2ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I2, 17);
+
+ //
+ // The content of timeZoneInfo.StandardDate is 8 words, which
+ // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
+ //
+ memcpyNoGCRefs(pResultArray->m_Array,
+ (LPVOID)&timeZoneInfo.DaylightDate,
+ 8 * sizeof(INT16));
+
+ //
+ // The content of timeZoneInfo.DaylightDate is 8 words, which
+ // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
+ //
+ memcpyNoGCRefs(((INT16*)pResultArray->m_Array) + 8,
+ (LPVOID)&timeZoneInfo.StandardDate,
+ 8 * sizeof(INT16));
+
+ ((INT16*)pResultArray->m_Array)[16] = (INT16)timeZoneInfo.DaylightBias * -1;
+
+lExit: ;
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(pResultArray);
+}
+FCIMPLEND
+
+#endif // FEATURE_CORECLR
+
+inline BOOL IsInvariantLocale(STRINGREF localeName)
+{
+ return localeName->GetStringLength() == 0;
+}
+
+// InternalChangeCaseChar
+//
+// Call LCMapStringEx with a char to make it upper or lower case
+// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
+FCIMPL5(FC_CHAR_RET, COMNlsInfo::InternalChangeCaseChar,
+ INT_PTR handle, // optional sort handle
+ INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ CLR_CHAR retVal = '\0';
+ int ret_LCMapStringEx = -1;
+
+ // Dereference our string
+ STRINGREF localeName(localeNameUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
+
+ BOOL isInvariantLocale = IsInvariantLocale(localeName);
+ // Check for Invariant to avoid A/V in LCMapStringEx
+ DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
+
+ handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ //For a versioned sort, Invariant should still use the OS
+ if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
+ {
+ ret_LCMapStringEx = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ &wch,
+ 1,
+ &retVal,
+ 1,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ ret_LCMapStringEx = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ &wch,
+ 1,
+ &retVal,
+ 1,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ ret_LCMapStringEx = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ &wch,
+ 1,
+ &retVal,
+ 1,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+
+ if (0 == ret_LCMapStringEx)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseChar! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
+ }
+
+ HELPER_METHOD_FRAME_END(); // localeName is now unprotected
+ return retVal;
+}
+FCIMPLEND
+
+// InternalChangeCaseString
+//
+// Call LCMapStringEx with a string to make it upper or lower case
+// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
+//
+// We typically expect the output string to be the same size as the input. If not
+// we have to count, reallocate the output buffer, and try again.
+FCIMPL5(Object*, COMNlsInfo::InternalChangeCaseString,
+ INT_PTR handle, // optional sort handle
+ INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pStringUNSAFE, CLR_BOOL bIsToUpper)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pStringUNSAFE));
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ } CONTRACTL_END;
+
+ struct _gc
+ {
+ STRINGREF pResult;
+ STRINGREF pString;
+ STRINGREF pLocale;
+ } gc;
+
+ gc.pResult = NULL;
+ gc.pString = ObjectToSTRINGREF(pStringUNSAFE);
+ gc.pLocale = ObjectToSTRINGREF(localeNameUNSAFE);
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc)
+
+ handle = EnsureValidSortHandle(handle, handleOrigin, gc.pLocale->GetBuffer());
+
+ //
+ // Get the length of the string.
+ //
+ int nLengthInput = gc.pString->GetStringLength();
+ int nLengthOutput = nLengthInput; // initially we assume the string length does not change.
+
+ BOOL isInvariantLocale = IsInvariantLocale(gc.pLocale);
+ // Check for Invariant to avoid A/V in LCMapStringEx
+ DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
+ // Check for Invariant to avoid A/V in LCMapStringEx
+
+ //
+ // Check if we have the empty string.
+ //
+ if (nLengthInput == 0)
+ {
+ gc.pResult = ObjectToSTRINGREF(gc.pString);
+ }
+ else
+ {
+ //
+ // Create the result string.
+ //
+ gc.pResult = StringObject::NewString(nLengthOutput);
+ LPWSTR pResultStr = gc.pResult->GetBuffer();
+
+ int result;
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ //Invariant should always use OS
+ if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
+ {
+ result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+
+ if(0 == result)
+ {
+ // Failure: Detect if that's due to insufficient buffer
+ if (GetLastError()!= ERROR_INSUFFICIENT_BUFFER)
+ {
+ ThrowLastError();
+ }
+ // need to update buffer
+#ifndef FEATURE_CORECLR
+ //Invariant should always use OS
+ if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
+ {
+ nLengthOutput = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ NULL,
+ 0,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ nLengthOutput = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ nLengthOutput = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ if (nLengthOutput == 0)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
+ }
+ _ASSERTE(nLengthOutput > 0);
+ // NOTE: The length of the required buffer does not include the terminating null character.
+ // So it can be used as-is for our calculations -- the length we pass in to NewString also does
+ // not include the terminating null character.
+ // MSDN documentation could be interpreted to mean that the length returned includes the terminating
+ // NULL character, but that's not the case.
+
+ // NOTE: Also note that we let the GC take care of the previously allocated pResult.
+
+ gc.pResult = StringObject::NewString(nLengthOutput);
+ pResultStr = gc.pResult->GetBuffer();
+#ifndef FEATURE_CORECLR
+ //Invariant should always use OS
+ if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
+ {
+ result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
+ bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
+ LCMAP_LOWERCASE | linguisticCasing,
+ gc.pString->GetBuffer(),
+ nLengthInput,
+ pResultStr,
+ nLengthOutput,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+
+ if(0 == result)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
+ }
+ }
+
+ pResultStr[nLengthOutput] = 0;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.pResult);
+}
+FCIMPLEND
+
+/*================================InternalGetCaseInsHash================================
+**Action:
+**Returns:
+**Arguments:
+**Exceptions:
+==============================================================================*/
+FCIMPL6(INT32, COMNlsInfo::InternalGetCaseInsHash,
+ INT_PTR handle, // optional sort handle
+ INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID pvStrA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(localeNameUNSAFE));
+ PRECONDITION(CheckPointer(pvStrA));
+ } CONTRACTL_END;
+
+ STRINGREF localeName = ObjectToSTRINGREF(localeNameUNSAFE);
+ STRINGREF strA;
+
+ INT32 result;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException))
+
+ *((LPVOID *)&strA)=pvStrA;
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+#endif
+
+ //
+ // If we know that we don't have any high characters (the common case) we can
+ // call a hash function that knows how to do a very fast case conversion. If
+ // we find characters above 0x80, it's much faster to convert the entire string
+ // to Uppercase and then call the standard hash function on it.
+ //
+ // TODO: NLS Arrowhead -We aren't consistent with the fast casing cultures (any en? fr? de?)
+ if (IsCultureEnglishOrInvariant((localeName)->GetBuffer()) && // If we're en-US or Invariant
+ ((IS_STRING_STATE_UNDETERMINED(strA->GetHighCharState()) && // and we're undetermined
+ IS_FAST_CASING(strA->InternalCheckHighChars())) || // and its fast casing when determined
+ IS_FAST_CASING(strA->GetHighCharState()))) // or we're fast casing that's already determined
+ {
+ // Notice that for Turkish and Azeri we don't get here and shouldn't use this
+ // fast path because of their special Latin casing rules.
+#ifndef FEATURE_CORECLR
+ result = curDomain->m_pNlsHashProvider->HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
+#else
+ result = COMNlsHashProvider::s_NlsHashProvider.HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
+#endif // FEATURE_CORECLR
+ }
+ else
+ {
+ handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
+
+ // Make it upper case
+ CQuickBytes newBuffer;
+ INT32 length = strA->GetStringLength();
+ WCHAR *pNewStr = (WCHAR *)newBuffer.AllocThrows((length + 1) * sizeof(WCHAR));
+
+ // Work around an A/V in LCMapStringEx for the invariant culture.
+ // Revisit this after Vista SP2 has been deployed everywhere.
+ DWORD linguisticCasing = 0;
+ if (localeName->GetStringLength() > 0) // if not the invariant culture...
+ {
+ linguisticCasing = LCMAP_LINGUISTIC_CASING;
+ }
+
+ int lcmapResult;
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ lcmapResult = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
+ LCMAP_UPPERCASE | linguisticCasing,
+ strA->GetBuffer(),
+ length,
+ pNewStr,
+ length,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ lcmapResult = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
+ LCMAP_UPPERCASE | linguisticCasing,
+ strA->GetBuffer(),
+ length,
+ pNewStr,
+ length,
+ NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ lcmapResult = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
+ LCMAP_UPPERCASE | linguisticCasing,
+ strA->GetBuffer(),
+ length,
+ pNewStr,
+ length,
+ NULL, NULL, (LPARAM) handle);
+ }
+
+ if (lcmapResult == 0)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::InternalGetCaseInsHash! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
+ }
+ pNewStr[length]='\0';
+
+ // Get hash for the upper case of the new string
+
+#ifndef FEATURE_CORECLR
+ result = curDomain->m_pNlsHashProvider->HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
+#else
+ result = COMNlsHashProvider::s_NlsHashProvider.HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
+#endif // FEATURE_CORECLR
+ }
+
+ END_SO_INTOLERANT_CODE
+
+ return result;
+}
+FCIMPLEND
+
+// Fast path for finding a String using OrdinalIgnoreCase rules
+// returns true if the fast path succeeded, with foundIndex set to the location where the String was found or -1
+// Returns false when FindStringOrdinal isn't handled (we don't have our own general version of this function to fall back on)
+// Note for future optimizations: kernel32!FindStringOrdinal(ignoreCase=TRUE) uses per-character table lookup
+// to map to upper case before comparison, but isn't otherwise optimized
+BOOL QCALLTYPE COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase(
+ __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH
+ __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
+ __in int cchSource, // number of characters lpStringSource after sourceIndex
+ __in int sourceIndex, // index from where the search will start in lpStringSource
+ __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
+ __in int cchValue,
+ __out int* foundIndex) // the index in lpStringSource where we found lpStringValue
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(lpStringSource != NULL);
+ PRECONDITION(lpStringValue != NULL);
+ PRECONDITION(cchSource>=0);
+ PRECONDITION(cchValue>=0);
+ PRECONDITION((dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION) == 0);
+ } CONTRACTL_END;
+
+ BOOL result = FALSE;
+
+ BEGIN_QCALL;
+
+ LPCWSTR lpSearchStart = NULL;
+ if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
+ {
+ lpSearchStart = &lpStringSource[sourceIndex - cchSource + 1];
+ }
+ else {
+ lpSearchStart = &lpStringSource[sourceIndex];
+ }
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ // Check if the default sorting is overridden
+ if (curDomain->m_pCustomSortLibrary != NULL)
+ {
+ *foundIndex = (curDomain->m_pCustomSortLibrary->pFindStringOrdinal)(
+ dwFindNLSStringFlags,
+ lpSearchStart,
+ cchSource,
+ lpStringValue,
+ cchValue,
+ TRUE);
+ result = TRUE;
+ }
+ else
+#endif
+ {
+#ifndef FEATURE_CORESYSTEM
+ // kernel function pointer
+ typedef int (WINAPI *PFNFindStringOrdinal)(DWORD, LPCWSTR, INT, LPCWSTR, INT, BOOL);
+ static PFNFindStringOrdinal FindStringOrdinal = NULL;
+
+ // initizalize kernel32!FindStringOrdinal
+ if (FindStringOrdinal == NULL)
+ {
+ PFNFindStringOrdinal result = NULL;
+
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod != NULL)
+ result=(PFNFindStringOrdinal)GetProcAddress(hMod,"FindStringOrdinal");
+
+ FindStringOrdinal = (result != NULL) ? result : (PFNFindStringOrdinal)-1;
+ }
+
+ // call into the kernel
+ if (FindStringOrdinal != (PFNFindStringOrdinal)-1)
+#endif
+ {
+ *foundIndex = FindStringOrdinal(
+ dwFindNLSStringFlags,
+ lpSearchStart,
+ cchSource,
+ lpStringValue,
+ cchValue,
+ TRUE);
+ result = TRUE;
+ }
+ }
+ // if we found the pattern string, fixup the index before we return
+ if (*foundIndex >= 0)
+ {
+ if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
+ *foundIndex += (sourceIndex - cchSource + 1);
+ else
+ *foundIndex += sourceIndex;
+ }
+ END_QCALL;
+
+ return result;
+}
+
+
+// InternalCompareStringOrdinalIgnoreCase
+//
+// Call ::CompareStringOrdinal for native ordinal behavior
+INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase(
+ LPCWSTR string1, INT32 index1,
+ LPCWSTR string2, INT32 index2,
+ INT32 length1,
+ INT32 length2)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(CheckPointer(string1));
+ PRECONDITION(CheckPointer(string2));
+ } CONTRACTL_END;
+
+ INT32 result = 0;
+
+ BEGIN_QCALL;
+ //
+ // Get the arguments.
+ // We assume the caller checked them before calling us
+ //
+
+ // We don't allow the -1 that native code allows
+ _ASSERT(length1 >= 0);
+ _ASSERT(length2 >= 0);
+
+ // Do the comparison
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+ if (curDomain->m_pCustomSortLibrary != NULL) {
+ result = (curDomain->m_pCustomSortLibrary->pCompareStringOrdinal)(string1 + index1, length1, string2 + index2, length2, TRUE);
+ }
+ else
+#endif
+ {
+ result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE);
+ }
+
+ // The native call shouldn't fail
+ _ASSERT(result != 0);
+ if (result == 0)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal! This is usually due to bad arguments.");
+ }
+
+ // Adjust the result to the expected -1, 0, 1 result
+ result -= 2;
+
+ END_QCALL;
+
+ return result;
+}
+
+/**
+ * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
+ * No error checking of any sort is performed. Range checking is entirely the responsibility of the managed
+ * code.
+ */
+FCIMPL0(EncodingDataItem *, COMNlsInfo::nativeGetEncodingTableDataPointer)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return (EncodingDataItem *)EncodingDataTable;
+}
+FCIMPLEND
+
+/**
+ * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
+ * No error checking of any sort is performed. Range checking is entirely the responsibility of the managed
+ * code.
+ */
+FCIMPL0(CodePageDataItem *, COMNlsInfo::nativeGetCodePageTableDataPointer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return ((CodePageDataItem*) CodePageDataTable);
+}
+FCIMPLEND
+
+
+#ifndef FEATURE_COREFX_GLOBALIZATION
+//
+// Normalization
+//
+
+FCIMPL6(int, COMNlsInfo::nativeNormalizationNormalizeString,
+ int NormForm, int& iError,
+ StringObject* inChars, int inLength,
+ CHARArray* outChars, int outLength )
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(inChars));
+ PRECONDITION(CheckPointer(outChars, NULL_OK));
+ } CONTRACTL_END;
+
+ // Dereference our string
+ STRINGREF inString(inChars);
+ LPWSTR inCharsBuffer = inString->GetBuffer();
+
+ CHARARRAYREF outCharArray(outChars);
+ LPWSTR outCharsBuffer = (outCharArray != NULL) ? ((LPWSTR) (outCharArray->GetDirectPointerToNonObjectElements())) : NULL;
+
+ // The OS APIs do not always set last error in success, so we have to do it explicitly
+ SetLastError(ERROR_SUCCESS);
+
+ int iResult = m_pfnNormalizationNormalizeStringFunc(
+ NormForm, inCharsBuffer, inLength, outCharsBuffer, outLength);
+
+ // Get our error if necessary
+ if (iResult <= 0)
+ {
+ // if the length is <= 0 there was an error
+ iError = GetLastError();
+
+ // Go ahead and return positive lengths/indexes so we don't get confused
+ iResult = -iResult;
+ }
+ else
+ {
+ iError = 0; // ERROR_SUCCESS
+ }
+
+ return iResult;
+}
+FCIMPLEND
+
+FCIMPL4( FC_BOOL_RET, COMNlsInfo::nativeNormalizationIsNormalizedString,
+ int NormForm, int& iError,
+ StringObject* chars, int inLength )
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(chars));
+ } CONTRACTL_END;
+
+ STRINGREF inString(chars);
+ LPWSTR charsBuffer = inString->GetBuffer();
+
+ // The OS APIs do not always set last error in success, so we have to do it explicitly
+ SetLastError(ERROR_SUCCESS);
+
+ // Ask if its normalized
+ BOOL bResult = m_pfnNormalizationIsNormalizedStringFunc( NormForm, charsBuffer, inLength);
+
+ // May need an error
+ if (bResult == false)
+ {
+ // If its false there may have been an error
+ iError = GetLastError();
+ }
+ else
+ {
+ iError = 0; // ERROR_SUCCESS
+ }
+
+ FC_RETURN_BOOL(bResult);
+}
+FCIMPLEND
+
+void QCALLTYPE COMNlsInfo::nativeNormalizationInitNormalization(int NormForm, BYTE* pTableData)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ if (m_hNormalization == NULL)
+ {
+ HMODULE hNormalization = NULL;
+
+ if (pTableData == NULL)
+ {
+ // Use OS implementation
+ hNormalization = GetModuleHandleW(W("kernel32.dll"));
+ if (!hNormalization)
+ ThrowLastError();
+ }
+#ifndef FEATURE_CORECLR
+ // in coreclr we should always find the normalization in kernel32 as it supports Win7 and up
+ else
+ {
+ HRESULT hr = g_pCLRRuntime->LoadLibrary(NORMALIZATION_DLL, &hNormalization);
+ if (FAILED(hr))
+ ThrowHR(hr);
+ }
+#endif // FEATURE_CORECLR
+
+ _ASSERTE(hNormalization != NULL);
+ m_hNormalization = hNormalization;
+ }
+
+ if (m_pfnNormalizationIsNormalizedStringFunc == NULL)
+ {
+ FARPROC pfn = GetProcAddress(m_hNormalization, "IsNormalizedString");
+ if (pfn == NULL)
+ ThrowLastError();
+ m_pfnNormalizationIsNormalizedStringFunc = (PFN_NORMALIZATION_IS_NORMALIZED_STRING)pfn;
+ }
+
+ if (m_pfnNormalizationNormalizeStringFunc == NULL)
+ {
+ FARPROC pfn = GetProcAddress(m_hNormalization, "NormalizeString");
+ if (pfn == NULL)
+ ThrowLastError();
+ m_pfnNormalizationNormalizeStringFunc = (PFN_NORMALIZATION_NORMALIZE_STRING)pfn;
+ }
+
+ if (pTableData != NULL)
+ {
+ if (m_pfnNormalizationInitNormalizationFunc == NULL)
+ {
+ FARPROC pfn = GetProcAddress(m_hNormalization, "InitNormalization");
+ if (pfn == NULL)
+ ThrowLastError();
+ m_pfnNormalizationInitNormalizationFunc = (PFN_NORMALIZATION_INIT_NORMALIZATION)pfn;
+ }
+
+ BYTE* pResult = m_pfnNormalizationInitNormalizationFunc( NormForm, pTableData);
+ if (pResult == NULL)
+ ThrowOutOfMemory();
+ }
+
+ END_QCALL;
+}
+
+#endif // FEATURE_COREFX_GLOBALIZATION
+
+
+//
+// This table should be sorted using case-insensitive ordinal order.
+// In the managed code, String.CompareStringOrdinalWC() is used to sort this.
+//
+
+
+/**
+ * This function returns the number of items in EncodingDataTable.
+ */
+FCIMPL0(INT32, COMNlsInfo::nativeGetNumEncodingItems)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return (m_nEncodingDataTableItems);
+}
+FCIMPLEND
+
+
+
+typedef CultureDataBaseObject* CULTUREDATAREF;
+
+// nativeInitCultureData checks with the OS to see if this is a valid culture.
+// If so we populate a limited number of fields. If its not valid we return false.
+//
+// The fields we populate:
+//
+// sWindowsName -- The name that windows thinks this culture is, ie:
+// en-US if you pass in en-US
+// de-DE_phoneb if you pass in de-DE_phoneb
+// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
+// fj if you pass in fj (neutral, post-Windows 7 machine)
+//
+// sRealName -- The name you used to construct the culture, in pretty form
+// en-US if you pass in EN-us
+// en if you pass in en
+// de-DE_phoneb if you pass in de-DE_phoneb
+//
+// sSpecificCulture -- The specific culture for this culture
+// en-US for en-US
+// en-US for en
+// de-DE_phoneb for alt sort
+// fj-FJ for fj (neutral)
+//
+// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
+// en-US if you pass in en-US
+// en if you pass in en
+// de-DE if you pass in de-DE_phoneb
+//
+// bNeutral -- TRUE if it is a neutral locale
+//
+// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
+// windows locale that's going to provide data for us.
+//
+FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeInitCultureData, CultureDataBaseObject *cultureDataUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ BOOL success=FALSE;
+
+ struct _gc
+ {
+ STRINGREF stringResult;
+ CULTUREDATAREF cultureData;
+ } gc;
+
+ gc.stringResult = NULL;
+ gc.cultureData = (CULTUREDATAREF) cultureDataUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+
+ WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
+ int result;
+
+ StackSString realNameBuffer( ((STRINGREF)gc.cultureData->sRealName)->GetBuffer() );
+
+ // Call GetLocaleInfoEx and see if the OS knows about it.
+ // Note that GetLocaleInfoEx has variations:
+ // * Pre-Vista it fails and has to go downlevel
+ // * Vista succeeds, but not for neutrals
+ // * Win7 succeeds for all locales.
+ // * Mac does ???
+ // The differences should be handled by the NewApis wrapper
+ result = NewApis::GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, buffer, NumItems(buffer));
+
+ // Did it fail?
+ if (result == 0)
+ {
+ // Not a real locale, fail
+ goto Exit;
+ }
+
+ // It worked, note that the name is the locale name, so use that (even for neutrals)
+ // We need to clean up our "real" name, which should look like the windows name right now
+ // so overwrite the input with the cleaned up name
+ gc.stringResult = StringObject::NewString(buffer, result-1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sRealName), gc.stringResult, NULL);
+
+ // Check for neutrality, don't expect to fail
+ // (buffer has our name in it, so we don't have to do the gc. stuff)
+ DWORD bNeutral;
+ if (0 == NewApis::GetLocaleInfoEx(buffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, (LPWSTR)&bNeutral, sizeof(bNeutral)/sizeof(WCHAR)))
+ goto Exit;
+
+ // Remember our neutrality
+ gc.cultureData->bNeutral = (bNeutral != 0);
+
+ gc.cultureData->bWin32Installed = (IsOSValidLocaleName(buffer, gc.cultureData->bNeutral) != 0);
+ gc.cultureData->bFramework = (IsWhidbeyFrameworkCulture(buffer) != 0);
+
+
+ // Note: Parents will be set dynamically
+
+ // Start by assuming the windows name'll be the same as the specific name since windows knows
+ // about specifics on all versions. For macs it also works. Only for downlevel Neutral locales
+ // does this have to change.
+ gc.stringResult = StringObject::NewString(buffer, result-1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
+
+ // Neutrals and non-neutrals are slightly different
+ if (gc.cultureData->bNeutral)
+ {
+ // Neutral Locale
+
+ // IETF name looks like neutral name
+ gc.stringResult = StringObject::NewString(buffer, result-1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
+
+ // Specific locale name is whatever ResolveLocaleName (win7+) returns.
+ // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
+ result = NewApis::ResolveLocaleName(buffer, buffer, NumItems(buffer));
+
+ // 0 is failure, 1 is invariant (""), which we expect
+ if (result < 1) goto Exit;
+
+ // We found a locale name, so use it.
+ // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
+ gc.stringResult = StringObject::NewString(buffer, result - 1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
+
+#ifdef FEATURE_CORECLR
+ if (!IsWindows7())
+ {
+ // For neutrals on Windows 7 + the neutral windows name can be the same as the neutral name,
+ // but on pre windows 7 names it has to be the specific, so we have to fix it in that case.
+ gc.stringResult = StringObject::NewString(buffer, result - 1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
+ }
+#endif
+
+
+ }
+ else
+ {
+ // Specific Locale
+
+ // Specific culture's the same as the locale name since we know its not neutral
+ // On mac we'll use this as well, even for neutrals. There's no obvious specific
+ // culture to use and this isn't exposed, but behaviorally this is correct on mac.
+ // Note that specifics include the sort name (de-DE_phoneb)
+ gc.stringResult = StringObject::NewString(buffer, result-1);
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
+
+ // We need the IETF name (sname)
+ // If we aren't an alt sort locale then this is the same as the windows name.
+ // If we are an alt sort locale then this is the same as the part before the _ in the windows name
+ // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
+
+ int localeNameLength = result - 1;
+
+ LCID lcid = NewApis::LocaleNameToLCID(buffer, 0);
+ if (!IsCustomCultureId(lcid))
+ {
+ LPCWSTR index = wcschr(buffer, W('_'));
+ if(index) // Not a custom culture and looks like an alt sort name
+ {
+ // Looks like an alt sort, and has a appropriate sort LCID (so not custom), make it smaller for the RFC 4646 style name
+ localeNameLength = static_cast<int>(index - buffer);
+ }
+ }
+
+ gc.stringResult = StringObject::NewString(buffer, localeNameLength);
+ _ASSERTE(gc.stringResult != NULL);
+
+ // Now use that name
+ SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
+ }
+
+#ifdef FEATURE_CORECLR
+ // For Silverlight make sure that the sorting tables are available (< Vista may not have east asian installed)
+ result = NewApis::CompareStringEx(((STRINGREF)gc.cultureData->sWindowsName)->GetBuffer(),
+ 0, W("A"), 1, W("B"), 1, NULL, NULL, 0);
+ if (result == 0) goto Exit;
+#endif
+
+ // It succeeded.
+ success = TRUE;
+
+Exit: {}
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(success);
+}
+FCIMPLEND
+
+
+// Return true if we're on Windows 7 (ie: if we have neutral native support)
+BOOL COMNlsInfo::IsWindows7()
+{
+ static BOOL bChecked=FALSE;
+ static BOOL bIsWindows7=FALSE;
+
+ if (!bChecked)
+ {
+ // LOCALE_INEUTRAL is first supported on Windows 7
+ if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEUTRAL, NULL, 0) != 0)
+ {
+ // Success, we're win7
+ bIsWindows7 = TRUE;
+ }
+
+ // Either way we checked now
+ bChecked = TRUE;
+ }
+
+ return bIsWindows7;
+}
+
+//
+// QCall implementation
+//
+int QCALLTYPE COMNlsInfo::InternalFindNLSStringEx(
+ __in_opt INT_PTR handle, // optional sort handle
+ __in_opt INT_PTR handleOrigin,
+ __in_z LPCWSTR lpLocaleName, // locale name
+ __in int dwFindNLSStringFlags, // search falg
+ __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
+ __in int cchSource, // number of characters lpStringSource after sourceIndex
+ __in int sourceIndex, // index from where the search will start in lpStringSource
+ __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
+ __in int cchValue) // length of the string we search for
+{
+ CONTRACTL {
+ QCALL_CHECK;
+ PRECONDITION(lpLocaleName != NULL);
+ PRECONDITION(lpStringSource != NULL);
+ PRECONDITION(lpStringValue != NULL);
+ PRECONDITION(cchSource>=0);
+ PRECONDITION(cchValue>=0);
+ } CONTRACTL_END;
+
+ int retValue = -1;
+
+ BEGIN_QCALL;
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+ handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
+#endif
+
+ #define RESERVED_FIND_ASCII_STRING 0x20000000 // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
+
+#ifndef FEATURE_CORECLR
+ int asciiFlag = (dwFindNLSStringFlags & RESERVED_FIND_ASCII_STRING);
+#endif // FEATURE_CORECLR
+
+ dwFindNLSStringFlags &= ~RESERVED_FIND_ASCII_STRING;
+
+ if (cchValue == 0)
+ {
+ retValue = sourceIndex; // keep Whidbey compatibility
+ goto lExit;
+ }
+
+ if (sourceIndex<0 || cchSource<0 ||
+ ((dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) && (sourceIndex+1<cchSource)))
+ {
+ goto lExit;
+ }
+
+ if (dwFindNLSStringFlags & COMPARE_OPTIONS_ORDINAL)
+ {
+ if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
+ {
+ retValue = NewApis::LastIndexOfString(
+ lpLocaleName,
+ &lpStringSource[sourceIndex - cchSource + 1],
+ cchSource,
+ lpStringValue,
+ cchValue,
+ dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
+ dwFindNLSStringFlags & FIND_ENDSWITH);
+ if (retValue >= 0)
+ {
+ retValue += sourceIndex - cchSource + 1;
+ }
+ }
+ else
+ {
+ retValue = NewApis::IndexOfString(
+ lpLocaleName,
+ &lpStringSource[sourceIndex],
+ cchSource,
+ lpStringValue,
+ cchValue,
+ dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
+ dwFindNLSStringFlags & FIND_STARTSWITH);
+
+ if (retValue >= 0)
+ {
+ retValue += sourceIndex;
+ }
+ }
+ }
+ else
+ {
+ if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
+ {
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
+ dwFindNLSStringFlags | asciiFlag,
+ &lpStringSource[sourceIndex - cchSource + 1],
+ cchSource,
+ lpStringValue,
+ cchValue,
+ NULL, NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
+ handle != NULL ? NULL : lpLocaleName,
+ dwFindNLSStringFlags,
+ &lpStringSource[sourceIndex - cchSource + 1],
+ cchSource,
+ lpStringValue,
+ cchValue, NULL, NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ retValue = NewApis::FindNLSStringEx(
+ handle != NULL ? NULL : lpLocaleName,
+ dwFindNLSStringFlags,
+ &lpStringSource[sourceIndex - cchSource + 1],
+ cchSource,
+ lpStringValue,
+ cchValue, NULL, NULL, NULL, (LPARAM) handle);
+ }
+
+ if (retValue >= 0)
+ {
+ retValue += sourceIndex - cchSource + 1;
+ }
+ }
+ else
+ {
+#ifndef FEATURE_CORECLR
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
+ dwFindNLSStringFlags | asciiFlag,
+ &lpStringSource[sourceIndex],
+ cchSource,
+ lpStringValue,
+ cchValue,
+ NULL, NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
+ handle != NULL ? NULL : lpLocaleName,
+ dwFindNLSStringFlags,
+ &lpStringSource[sourceIndex],
+ cchSource,
+ lpStringValue,
+ cchValue, NULL, NULL, NULL, (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ retValue = NewApis::FindNLSStringEx(
+ handle != NULL ? NULL : lpLocaleName,
+ dwFindNLSStringFlags,
+ &lpStringSource[sourceIndex],
+ cchSource,
+ lpStringValue,
+ cchValue, NULL, NULL, NULL, (LPARAM) handle);
+ }
+
+ if (retValue >= 0)
+ {
+ retValue += sourceIndex;
+ }
+ }
+ }
+
+lExit:
+
+ END_QCALL;
+
+ return retValue;
+}
+
+
+int QCALLTYPE COMNlsInfo::InternalGetSortKey(
+ __in_opt INT_PTR handle, // PSORTHANDLE
+ __in_opt INT_PTR handleOrigin,
+ __in_z LPCWSTR pLocaleName, // locale name
+ __in int flags, // flags
+ __in_ecount(cchSource) LPCWSTR pStringSource, // Source string
+ __in int cchSource, // number of characters in lpStringSource
+ __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count)
+ __in int cchTarget) // Character count for target buffer
+{
+ CONTRACTL {
+ QCALL_CHECK;
+ PRECONDITION(pLocaleName != NULL);
+ PRECONDITION(pStringSource != NULL);
+// PRECONDITION(pTarget != NULL);
+ PRECONDITION(cchSource>=0);
+// PRECONDITION(cchTarget>=0);
+ } CONTRACTL_END;
+
+ int retValue = 0;
+
+ BEGIN_QCALL;
+
+
+#ifndef FEATURE_CORECLR
+ handle = EnsureValidSortHandle(handle, handleOrigin, pLocaleName);
+
+ AppDomain* curDomain = GetAppDomain();
+
+ if(!(curDomain->m_bUseOsSorting))
+ {
+ retValue = SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle,
+ flags,
+ pStringSource,
+ cchSource,
+ pTarget,
+ cchTarget,
+ NULL, 0);
+ }
+ else if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ retValue = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : pLocaleName,
+ flags | LCMAP_SORTKEY,
+ pStringSource,
+ cchSource,
+ (LPWSTR)pTarget,
+ cchTarget,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ else
+#endif
+ {
+ // Just call NewApis::LCMapStringEx to do our work
+ retValue = NewApis::LCMapStringEx(handle != NULL ? NULL : pLocaleName,
+ flags | LCMAP_SORTKEY,
+ pStringSource,
+ cchSource,
+ (LPWSTR)pTarget,
+ cchTarget,
+ NULL,
+ NULL,
+ (LPARAM) handle);
+ }
+ END_QCALL;
+
+ return retValue;
+}
+
+
+// We allow InternalInitSortHandle to return a NULL value
+// this is the case for Silverlight or when the appdomain has custom sorting.
+// So all the methods that take a SortHandle, also have to
+// be able to just call the slower api that looks up the tables based on the locale name
+INT_PTR QCALLTYPE COMNlsInfo::InternalInitSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
+{
+ CONTRACTL {
+ QCALL_CHECK;
+ PRECONDITION(localeName != NULL);
+ } CONTRACTL_END;
+
+ INT_PTR pSort = NULL;
+
+ BEGIN_QCALL;
+
+ pSort = InitSortHandleHelper(localeName, handleOrigin);
+
+ END_QCALL;
+
+ return pSort;
+}
+
+INT_PTR COMNlsInfo::InitSortHandleHelper(LPCWSTR localeName, __out INT_PTR* handleOrigin)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CANNOT_TAKE_LOCK;
+ PRECONDITION(localeName != NULL);
+ } CONTRACTL_END;
+
+ INT_PTR pSort = NULL;
+
+#ifndef FEATURE_CORECLR
+ AppDomain* curDomain = GetAppDomain();
+
+#if _DEBUG
+ _ASSERTE(curDomain->m_bSortingInitialized);
+#endif
+
+ if(curDomain->m_bUseOsSorting)
+ {
+ pSort = InternalInitOsSortHandle(localeName, handleOrigin);
+ }
+ else
+ {
+ pSort = InternalInitVersionedSortHandle(localeName, handleOrigin);
+ }
+#else
+ // coreclr will try to initialize the handle and if running on downlevel it'll just return null
+ pSort = InternalInitOsSortHandle(localeName, handleOrigin);
+#endif // FEATURE_CORECLR
+ return pSort;
+}
+
+#ifndef FEATURE_CORECLR
+INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CANNOT_TAKE_LOCK;
+ PRECONDITION(localeName != NULL);
+ } CONTRACTL_END;
+
+ AppDomain* curDomain = GetAppDomain();
+
+ if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ return NULL;
+ }
+
+ return InternalInitVersionedSortHandle(localeName, handleOrigin, curDomain->m_sortVersion);
+}
+
+INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin, DWORD sortVersion)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CANNOT_TAKE_LOCK;
+ PRECONDITION(localeName != NULL);
+ } CONTRACTL_END;
+
+ INT_PTR pSort = NULL;
+
+ _ASSERTE(NewApis::NotLeakingFrameworkOnlyCultures(localeName));
+
+ *handleOrigin = (INT_PTR) SortVersioning::GetSortHandle;
+
+ // try the requested version
+ if(sortVersion != DEFAULT_SORT_VERSION)
+ {
+ NLSVERSIONINFO sortVersionInfo;
+ sortVersionInfo.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
+ sortVersionInfo.dwNLSVersion = sortVersion;
+ sortVersionInfo.dwDefinedVersion = sortVersion;
+ pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, &sortVersionInfo);
+ }
+
+ // fallback to default version
+ if(pSort == NULL)
+ {
+ pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, NULL);
+ }
+
+ _ASSERTE(RunningOnWin8() || pSort != NULL);
+
+ return pSort;
+}
+#endif //FEATURE_CORECLR
+
+// Can return NULL
+INT_PTR COMNlsInfo::InternalInitOsSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CANNOT_TAKE_LOCK;
+ PRECONDITION(localeName != NULL);
+ } CONTRACTL_END;
+
+ INT_PTR pSort = NULL;
+
+ AppDomain* curDomain = GetAppDomain();
+
+#ifndef FEATURE_CORECLR
+ if (RunningOnWin8() || curDomain->m_pCustomSortLibrary != NULL)
+#else
+ if (RunningOnWin8())
+#endif //FEATURE_CORECLR
+ {
+ LPARAM lSortHandle;
+ int ret;
+
+#ifndef FEATURE_CORECLR
+ if (curDomain->m_pCustomSortLibrary != NULL)
+ {
+ ret = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
+ *handleOrigin = (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx;
+ }
+ else
+#endif //FEATURE_CORECLR
+ {
+ ret = NewApis::LCMapStringEx(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
+ *handleOrigin = (INT_PTR) NewApis::LCMapStringEx;
+ }
+
+ if (ret != 0)
+ {
+ pSort = (INT_PTR) lSortHandle;
+ }
+ }
+
+ return pSort;
+}
+
+#ifndef FEATURE_CORECLR
+BOOL QCALLTYPE COMNlsInfo::InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation)
+{
+ CONTRACTL {
+ QCALL_CHECK;
+ } CONTRACTL_END;
+
+ BOOL ret = FALSE;
+
+ BEGIN_QCALL;
+
+ AppDomain* curDomain = GetAppDomain();
+
+ if(curDomain->m_bUseOsSorting)
+ {
+ if(curDomain->m_pCustomSortLibrary != NULL)
+ {
+ ret = (curDomain->m_pCustomSortLibrary->pGetNLSVersionEx)(COMPARE_STRING, lpLocaleName, lpVersionInformation);
+ }
+ else
+ {
+ ret = GetNLSVersionEx(COMPARE_STRING, lpLocaleName, lpVersionInformation);
+ }
+ }
+ else
+ {
+ handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
+
+ NLSVERSIONINFO nlsVersion;
+ nlsVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
+
+ ret = SortVersioning::SortGetNLSVersion((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, &nlsVersion);
+
+ lpVersionInformation->dwNLSVersion = nlsVersion.dwNLSVersion;
+ lpVersionInformation->dwDefinedVersion = nlsVersion.dwDefinedVersion;
+ lpVersionInformation->dwEffectiveId = 0;
+ ZeroMemory(&(lpVersionInformation->guidCustomVersion), sizeof(GUID));
+ }
+
+ END_QCALL;
+
+ return ret;
+}
+
+DWORD QCALLTYPE COMNlsInfo::InternalGetSortVersion()
+{
+ CONTRACTL {
+ QCALL_CHECK;
+ } CONTRACTL_END;
+
+ DWORD version = DEFAULT_SORT_VERSION;
+
+ BEGIN_QCALL;
+
+ AppDomain* curDomain = GetAppDomain();
+ version = curDomain->m_sortVersion;
+
+ END_QCALL;
+
+ return version;
+}
+
+#endif //FEATURE_CORECLR
diff --git a/src/classlibnative/nls/nlstable.cpp b/src/classlibnative/nls/nlstable.cpp
new file mode 100644
index 0000000000..dd395153d4
--- /dev/null
+++ b/src/classlibnative/nls/nlstable.cpp
@@ -0,0 +1,259 @@
+// 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.
+
+#include "common.h"
+#include <winwrap.h>
+#include <excep.h> // For COMPlusThrow
+#include <appdomain.hpp>
+#include <assembly.hpp>
+#include "nlstable.h" // Class declaration
+
+#if FEATURE_CODEPAGES_FILE
+
+/*=================================CreateSharedMemoryMapping==================================
+**Action: Create a file mapping object which can be shared among different users under Windows NT/2000.
+** Actually its just a memory mapped section of the swapfile.
+**Returns: The file mapping handle. NULL if any error happens.
+**Arguments:
+** pMappingName the name of the file mapping object.
+** iSize Size to use
+**Exceptions:
+**Note:
+** This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
+** Then create a security descriptor using this DACL. Finally, use this SA to create the file mapping object.
+** WARNING:
+** This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
+** Therefore for side-by-side to work, the mapping names must be unique per version!
+** We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
+** tables, but it seems suspicious for other applications (as commented in MapDataFile below)
+==============================================================================*/
+// static method
+HANDLE NLSTable::CreateSharedMemoryMapping(const LPCWSTR pMappingName, const int iSize ) {
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(iSize > 0);
+ PRECONDITION(CheckPointer(pMappingName));
+ } CONTRACTL_END;
+
+ HANDLE hFileMap = NULL;
+
+ SECURITY_DESCRIPTOR sd ;
+ SECURITY_ATTRIBUTES sa ;
+
+ //
+ // Create the sid for the Everyone group.
+ //
+ SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
+ PSID pSID = NULL;
+ int nSidSize;
+
+ PACL pDACL = NULL;
+ int nAclSize;
+
+ CQuickBytes newBuffer;
+
+ if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSID)) {
+ goto ErrorExit;
+ }
+
+ nSidSize = GetLengthSid(pSID);
+
+ //
+ // Create Discretionary Access Control List (DACL).
+ //
+
+ // First calculate the size of the DACL, since this is a linked-list like structure which contains one or more
+ // ACE (access control entry)
+ nAclSize = sizeof(ACL) // the header structure of ACL
+ + sizeof(ACCESS_ALLOWED_ACE) + nSidSize; // and one "access allowed ACE".
+
+ // We know the size needed for DACL now, so create it.
+ // An exception is thrown if OOM happens.
+ pDACL = (PACL) (newBuffer.AllocThrows(nAclSize));
+ if(!InitializeAcl( pDACL, nAclSize, ACL_REVISION ))
+ goto ErrorExit;
+
+ // Add the "access allowed ACE", meaning:
+ // we will allow members of the "Everyone" group to have SECTION_MAP_READ | SECTION_QUERY access to the file mapping object.
+ // for memory sections the creator will still be allowed to create it.
+ if(!AddAccessAllowedAce( pDACL, ACL_REVISION, SECTION_MAP_READ | SECTION_QUERY, pSID ))
+ goto ErrorExit;
+
+ //
+ // Create Security descriptor (SD).
+ //
+ if(!InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ))
+ goto ErrorExit;
+ // Set the previously created DACL to this SD.
+ if(!SetSecurityDescriptorDacl( &sd, TRUE, pDACL, FALSE ))
+ goto ErrorExit;
+
+ // Create Security Attribute (SA).
+ sa.nLength = sizeof( sa ) ;
+ sa.bInheritHandle = TRUE ;
+ sa.lpSecurityDescriptor = &sd ;
+
+ //
+ // Finally, create the file mapping using the SA.
+ //
+
+ // If we are on Windows 2000 or later, try to open it in global namespace. The \global namespace is ignored if
+ // Terminal service is not running.
+ WCHAR globalSectionName[MAX_LONGPATH];
+ wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
+ if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH) {
+ goto ErrorExit;
+ }
+ wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
+
+ // Try to create the section in the Global\ namespace. The CreateFileMapping() will ignore Global\ namespace if Terminal Service
+ // is not running.
+ hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, globalSectionName);
+
+ // If not allowed to be global (like terminal server) or not WinNT, then open it in local namespace.
+ if (hFileMap == NULL)
+ {
+ // Not WinNT or access denied for Global\ namespace, try the local namespace. When Terminal service is running, the Local\ namespace
+ // means the namespace "Sessions\<n>\BasedNamedObjects".
+ hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, pMappingName);
+ }
+
+ErrorExit:
+ if(pSID)
+ FreeSid( pSID );
+
+ // If still not allowed, try building one with no name
+ if (hFileMap == NULL)
+ {
+ hFileMap = WszCreateFileMapping(
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, iSize, NULL);
+ }
+
+ return (hFileMap) ;
+}
+
+/*=================================OpenOrCreateMemoryMapping==================================
+**Action: Opens an existing memory mapped object, or creates a new one (by calling above fn).
+** Worst case just allocate some memory.
+**Returns: The pointer to our memory. NULL if any error happens.
+**Arguments:
+** pMappingName the name of the file mapping object.
+** iSize Size to use
+**Exceptions:
+**
+**IMPORTANT:
+** Memory mapped sections are cleared when set. We expect the caller to set the last int
+** to a non-zero value, so we test this flag. If it is still zero when we open it, we
+** assume that we've gotten a result in an unfinished state and allocate a new one instead
+** of trying to use the one with the zeros.
+**
+**Note:
+** This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
+** Then create a security descriptor using this DACL. Finally, use this SA to create the file mapping object.
+** WARNING:
+** This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
+** Therefore for side-by-side to work, the mapping names must be unique per version!
+** We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
+** tables, but it seems suspicious for other applications (as commented in MapDataFile below)
+==============================================================================*/
+PBYTE NLSTable::OpenOrCreateMemoryMapping(const LPCWSTR pMappingName, const int iSize, HANDLE* mappedFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ DISABLED(GC_TRIGGERS); //
+ MODE_ANY;
+ PRECONDITION(iSize % 4 == 0);
+ PRECONDITION(iSize > 0);
+ PRECONDITION(CheckPointer(pMappingName));
+ PRECONDITION(CheckPointer(mappedFile));
+ } CONTRACTL_END;
+
+ _ASSERTE(pMappingName != NULL); // Must have a string name.
+ _ASSERTE(iSize > 0); // Pointless to have <= 0 allocation
+ _ASSERTE(iSize % 4 == 0); // Need 4 byte alignment for flag check
+
+ LPVOID pResult = NULL;
+
+ // Try creating/opening it.
+ HANDLE hMap = NULL;
+
+ *mappedFile = hMap;
+ // Calls into OS a lot, should switch to preemp mode
+ GCX_PREEMP();
+
+ // First try opening it. It might already be in existence
+ // The assumption here is that it's rare that we will hit the cases where two or more threads are trying to create
+ // the named section at the same time. Therefore, the following code does not use critical section or mutex trying
+ // to synchornize different threads.
+
+ // Try to open it in global namespace. The global\ namespace is ignored if terminal service is not running.
+ WCHAR globalSectionName[MAX_LONGPATH];
+ wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
+ if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH)
+ return NULL;
+
+ wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
+
+ hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, globalSectionName);
+ if (hMap == NULL) {
+ // If access is denied for global\namespace or the name is not opened in global namespace, try the local namespace.
+ // Also if we're rotor or win 9x.
+ hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, pMappingName);
+ }
+
+ if (hMap != NULL) {
+ // We got a section, map a view, READ ONLY!
+ pResult = MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0);
+
+ // Anything found?
+ if (pResult != NULL)
+ {
+ // Make sure our result is allocated. We expect a non-0 flag to be set for last int of our section
+ const int* pFlag = (int*)(((BYTE*)pResult) + iSize - 4);
+ if (*pFlag != 0)
+ {
+ *mappedFile = hMap;
+ // Found a valid already opened section!
+ return (PBYTE)pResult;
+ }
+
+ // Couldn't find it, unmap it.
+ UnmapViewOfFile(pResult);
+ pResult = NULL;
+ }
+
+ // We can't use this one, so close it
+ CloseHandle(hMap);
+ hMap = NULL;
+ }
+
+ // Didn't get a section, try to create one, NT/XP/.Net gets security permissions, 9X doesn't,
+ // but our helper fn takes care of that for us.
+ hMap = NLSTable::CreateSharedMemoryMapping(pMappingName, iSize);
+
+ // Were we successfull?
+ if (hMap != NULL)
+ {
+ // We have hMap, try to get our section
+ pResult = MapViewOfFile( hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+
+ // Don't close hMap unless we aren't using it.
+ // That confuses the mapping stuff and we lose the name, it'll close when runtime shuts down.
+ if (pResult == NULL)
+ {
+ CloseHandle(hMap);
+ hMap = NULL;
+ }
+ // There is no need to zero out the mapCodePageCached field, since the initial contents of the pages in the file mapping object are zero.
+
+ *mappedFile = hMap;
+ }
+
+ return (PBYTE)pResult;
+}
+
+#endif