Move String to shared corelib partition (#16749)
authorJan Kotas <jkotas@microsoft.com>
Thu, 8 Mar 2018 14:09:03 +0000 (06:09 -0800)
committerGitHub <noreply@github.com>
Thu, 8 Mar 2018 14:09:03 +0000 (06:09 -0800)
19 files changed:
src/classlibnative/bcltype/stringnative.cpp
src/classlibnative/bcltype/stringnative.h
src/mscorlib/Resources/Strings.resx
src/mscorlib/System.Private.CoreLib.csproj
src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs [new file with mode: 0644]
src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
src/mscorlib/shared/System/String.cs [new file with mode: 0644]
src/mscorlib/src/System/Buffer.cs
src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
src/mscorlib/src/System/RtType.cs
src/mscorlib/src/System/String.CoreCLR.cs [new file with mode: 0644]
src/mscorlib/src/System/String.cs [deleted file]
src/vm/ecall.cpp
src/vm/ecall.h
src/vm/ecalllist.h
src/vm/metasig.h
src/vm/mscorlib.h
src/vm/object.cpp
src/vm/object.h

index cc91d01..63eed73 100644 (file)
 #pragma optimize("tgy", on)
 #endif
 
-//
-//
-//  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
-
 inline COMNlsHashProvider * GetCurrentNlsHashProvider()
 {
     LIMITED_METHOD_CONTRACT;
index 53b7b86..f0df050 100644 (file)
 
 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
     //
index c857217..28346b2 100644 (file)
   <data name="Arg_MustBeString" xml:space="preserve">
     <value>Object must be of type String.</value>
   </data>
-  <data name="Arg_MustBeStringPtrNotAtom" xml:space="preserve">
-    <value>The pointer passed in as a String must not be in the bottom 64K of the process's address space.</value>
-  </data>
   <data name="Arg_MustBeTimeSpan" xml:space="preserve">
     <value>Object must be of type TimeSpan.</value>
   </data>
   <data name="Arg_InsufficientNumberOfElements" xml:space="preserve">
     <value>At least {0} element(s) are expected in the parameter "{1}".</value>
   </data>
-</root>
\ No newline at end of file
+  <data name="Arg_MustBeNullTerminatedString" xml:space="preserve">
+    <value>The string must be null-terminated.</value>
+  </data>
+</root>
index 46ccdf1..46f46de 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Object.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Array.cs" />
     <Compile Include="$(BclSourcesRoot)\System\ThrowHelper.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\String.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\String.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\String.Comparison.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Text\StringBuilder.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Text\StringBuilderCache.cs" />
diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs
new file mode 100644 (file)
index 0000000..158e4db
--- /dev/null
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal partial class Interop
+{
+    internal partial class Kernel32
+    {
+        [DllImport(Libraries.Kernel32)]
+        internal static extern unsafe int MultiByteToWideChar(
+            uint CodePage, uint dwFlags,
+            byte* lpMultiByteStr, int cbMultiByte,
+            char* lpWideCharStr, int cchWideChar);
+
+        internal const uint MB_PRECOMPOSED = 0x00000001;
+    }
+}
index a0c6c0c..a5b49c8 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.BinarySearch.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\String.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\String.Searching.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MultiByteToWideChar.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.OutputDebugString.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs" />
diff --git a/src/mscorlib/shared/System/String.cs b/src/mscorlib/shared/System/String.cs
new file mode 100644 (file)
index 0000000..e303b43
--- /dev/null
@@ -0,0 +1,750 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Text;
+
+namespace System
+{
+    // The String class represents a static string of characters.  Many of
+    // the String methods perform some type of transformation on the current
+    // instance and return the result as a new String.  As with arrays, character
+    // positions (indices) are zero-based.
+
+    [Serializable]
+    [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+    public sealed partial class String : IComparable, IEnumerable, IEnumerable<char>, IComparable<String>, IEquatable<String>, IConvertible, ICloneable
+    {
+        // String constructors
+        // These are special. The implementation methods for these have a different signature from the
+        // declared constructors.
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern String(char[] value);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private string Ctor(char[] value)
+        {
+            if (value == null || value.Length == 0)
+                return Empty;
+
+            string result = FastAllocateString(value.Length);
+            unsafe
+            {
+                fixed (char* dest = &result._firstChar, source = value)
+                    wstrcpy(dest, source, value.Length);
+            }
+            return result;
+        }
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern String(char[] value, int startIndex, int length);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private string Ctor(char[] value, int startIndex, int length)
+        {
+            if (value == null)
+                throw new ArgumentNullException(nameof(value));
+
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+
+            if (length < 0)
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+
+            if (startIndex > value.Length - length)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+
+            if (length == 0)
+                return Empty;
+
+            string result = FastAllocateString(length);
+            unsafe
+            {
+                fixed (char* dest = &result._firstChar, source = value)
+                    wstrcpy(dest, source + startIndex, length);
+            }
+            return result;
+        }
+
+        [CLSCompliant(false)]
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern unsafe String(char* value);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(char* ptr)
+        {
+            if (ptr == null)
+                return Empty;
+
+            int count = wcslen(ptr);
+            if (count == 0)
+                return Empty;
+
+            string result = FastAllocateString(count);
+            fixed (char* dest = &result._firstChar)
+                wstrcpy(dest, ptr, count);
+            return result;
+        }
+
+        [CLSCompliant(false)]
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern unsafe String(char* value, int startIndex, int length);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(char* ptr, int startIndex, int length)
+        {
+            if (length < 0)
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+
+            char* pStart = ptr + startIndex;
+
+            // overflow check
+            if (pStart < ptr)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
+
+            if (length == 0)
+                return Empty;
+
+            if (ptr == null)
+                throw new ArgumentOutOfRangeException(nameof(ptr), SR.ArgumentOutOfRange_PartialWCHAR);
+
+            string result = FastAllocateString(length);
+            fixed (char* dest = &result._firstChar)
+                wstrcpy(dest, pStart, length);
+            return result;
+        }
+
+        [CLSCompliant(false)]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public extern unsafe String(sbyte* value);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(sbyte* value)
+        {
+            byte* pb = (byte*)value;
+            if (pb == null)
+                return Empty;
+
+            int numBytes = new ReadOnlySpan<byte>((byte*)value, int.MaxValue).IndexOf<byte>(0);
+
+#if BIT64
+            // Check for overflow
+            if (numBytes < 0)
+                throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
+#else
+            Debug.Assert(numBytes >= 0);
+#endif
+
+            return CreateStringForSByteConstructor(pb, numBytes);
+        }
+
+        [CLSCompliant(false)]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public extern unsafe String(sbyte* value, int startIndex, int length);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(sbyte* value, int startIndex, int length)
+        {
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+
+            if (length < 0)
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+
+            if (value == null)
+                throw new ArgumentNullException(nameof(value));
+
+            byte* pStart = (byte*)(value + startIndex);
+
+            // overflow check
+            if (pStart < value)
+                throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_PartialWCHAR);
+
+            return CreateStringForSByteConstructor(pStart, length);
+        }
+
+        // Encoder for String..ctor(sbyte*) and String..ctor(sbyte*, int, int)
+        private static unsafe string CreateStringForSByteConstructor(byte *pb, int numBytes)
+        {
+            Debug.Assert(numBytes >= 0);
+            Debug.Assert(pb <= (pb + numBytes));
+
+            if (numBytes == 0)
+                return Empty;
+
+#if PLATFORM_UNIX
+            return Encoding.UTF8.GetString(pb, numBytes);
+#else
+            int numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, (char*)null, 0);
+            if (numCharsRequired == 0)
+                throw new ArgumentException(SR.Arg_InvalidANSIString);
+
+            string newString = FastAllocateString(numCharsRequired);
+            fixed (char *pFirstChar = &newString._firstChar)
+            {
+                numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, pFirstChar, numCharsRequired);
+            }
+            if (numCharsRequired == 0)
+                throw new ArgumentException(SR.Arg_InvalidANSIString);
+            return newString;
+#endif
+        }
+
+        [CLSCompliant(false)]
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public extern unsafe String(sbyte* value, int startIndex, int length, Encoding enc);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding enc)
+        {
+            if (enc == null)
+                return new string(value, startIndex, length);
+
+            if (length < 0)
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+            if (startIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+
+            byte* pStart = (byte*)(value + startIndex);
+
+            // overflow check
+            if (pStart < value)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
+
+            return enc.GetString(new ReadOnlySpan<byte>(pStart, length));
+        }
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern String(char c, int count);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private string Ctor(char c, int count)
+        {
+            if (count <= 0)
+            {
+                if (count == 0)
+                    return Empty;
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
+            }
+
+            string result = FastAllocateString(count);
+
+            if (c != '\0') // Fast path null char string
+            {
+                unsafe
+                {
+                    fixed (char* dest = &result._firstChar)
+                    {
+                        uint cc = (uint)((c << 16) | c);
+                        uint* dmem = (uint*)dest;
+                        if (count >= 4)
+                        {
+                            count -= 4;
+                            do
+                            {
+                                dmem[0] = cc;
+                                dmem[1] = cc;
+                                dmem += 2;
+                                count -= 4;
+                            } while (count >= 0);
+                        }
+                        if ((count & 2) != 0)
+                        {
+                            *dmem = cc;
+                            dmem++;
+                        }
+                        if ((count & 1) != 0)
+                            ((char*)dmem)[0] = c;
+                    }
+                }
+            }
+            return result;
+        }
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public extern String(ReadOnlySpan<char> value);
+
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
+#if !CORECLR
+        static
+#endif
+        private unsafe string Ctor(ReadOnlySpan<char> value)
+        {
+            if (value.Length == 0)
+                return Empty;
+
+            string result = FastAllocateString(value.Length);
+            fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value))
+                wstrcpy(dest, src, value.Length);
+            return result;
+        }
+
+        public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
+        {
+            if (action == null)
+                throw new ArgumentNullException(nameof(action));
+
+            if (length <= 0)
+            {
+                if (length == 0)
+                    return Empty;
+                throw new ArgumentOutOfRangeException(nameof(length));
+            }
+
+            string result = FastAllocateString(length);
+            action(new Span<char>(ref result.GetRawStringData(), length), state);
+            return result;
+        }
+
+        public static implicit operator ReadOnlySpan<char>(string value) =>
+            value != null ? new ReadOnlySpan<char>(ref value.GetRawStringData(), value.Length) : default;
+
+        public object Clone()
+        {
+            return this;
+        }
+
+        public static unsafe string Copy(string str)
+        {
+            if (str == null)
+                throw new ArgumentNullException(nameof(str));
+
+            string result = FastAllocateString(str.Length);
+            fixed (char* dest = &result._firstChar, src = &str._firstChar)
+                wstrcpy(dest, src, str.Length);
+            return result;
+        }
+
+        // Converts a substring of this string to an array of characters.  Copies the
+        // characters of this string beginning at position sourceIndex and ending at
+        // sourceIndex + count - 1 to the character array buffer, beginning
+        // at destinationIndex.
+        //
+        public unsafe void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
+        {
+            if (destination == null)
+                throw new ArgumentNullException(nameof(destination));
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
+            if (sourceIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
+            if (count > Length - sourceIndex)
+                throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_IndexCount);
+            if (destinationIndex > destination.Length - count || destinationIndex < 0)
+                throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_IndexCount);
+
+            fixed (char* src = &_firstChar, dest = destination)
+                wstrcpy(dest + destinationIndex, src + sourceIndex, count);
+        }
+
+        // Returns the entire string as an array of characters.
+        public unsafe char[] ToCharArray()
+        {
+            if (Length == 0)
+                return Array.Empty<char>();
+
+            char[] chars = new char[Length];
+            fixed (char* src = &_firstChar, dest = &chars[0])
+                wstrcpy(dest, src, Length);
+            return chars;
+        }
+
+        // Returns a substring of this string as an array of characters.
+        //
+        public unsafe char[] ToCharArray(int startIndex, int length)
+        {
+            // Range check everything.
+            if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
+                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+
+            if (length <= 0)
+            {
+                if (length == 0)
+                    return Array.Empty<char>();
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
+            }
+
+            char[] chars = new char[length];
+            fixed (char* src = &_firstChar, dest = &chars[0])
+                wstrcpy(dest, src + startIndex, length);
+            return chars;
+        }
+
+        [NonVersionable]
+        public static bool IsNullOrEmpty(string value)
+        {
+            return (value == null || value.Length == 0);
+        }
+
+        public static bool IsNullOrWhiteSpace(string value)
+        {
+            if (value == null) return true;
+
+            for (int i = 0; i < value.Length; i++)
+            {
+                if (!Char.IsWhiteSpace(value[i])) return false;
+            }
+
+            return true;
+        }
+
+        internal ref char GetRawStringData() => ref _firstChar;
+
+        // Helper for encodings so they can talk to our buffer directly
+        // stringLength must be the exact size we'll expect
+        internal unsafe static string CreateStringFromEncoding(
+            byte* bytes, int byteLength, Encoding encoding)
+        {
+            Debug.Assert(bytes != null);
+            Debug.Assert(byteLength >= 0);
+
+            // Get our string length
+            int stringLength = encoding.GetCharCount(bytes, byteLength, null);
+            Debug.Assert(stringLength >= 0, "stringLength >= 0");
+
+            // They gave us an empty string if they needed one
+            // 0 bytelength might be possible if there's something in an encoder
+            if (stringLength == 0)
+                return Empty;
+
+            string s = FastAllocateString(stringLength);
+            fixed (char* pTempChars = &s._firstChar)
+            {
+                int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
+                Debug.Assert(stringLength == doubleCheck,
+                    "Expected encoding.GetChars to return same length as encoding.GetCharCount");
+            }
+
+            return s;
+        }
+
+        // This is only intended to be used by char.ToString.
+        // It is necessary to put the code in this class instead of Char, since _firstChar is a private member.
+        // Making _firstChar internal would be dangerous since it would make it much easier to break String's immutability.
+        internal static string CreateFromChar(char c)
+        {
+            string result = FastAllocateString(1);
+            result._firstChar = c;
+            return result;
+        }
+
+        internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
+        {
+            Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
+        }
+
+
+        // Returns this string.
+        public override string ToString()
+        {
+            return this;
+        }
+
+        // Returns this string.
+        public string ToString(IFormatProvider provider)
+        {
+            return this;
+        }
+
+        public CharEnumerator GetEnumerator()
+        {
+            return new CharEnumerator(this);
+        }
+
+        IEnumerator<char> IEnumerable<char>.GetEnumerator()
+        {
+            return new CharEnumerator(this);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return new CharEnumerator(this);
+        }
+
+        internal static unsafe int wcslen(char* ptr)
+        {
+            char* end = ptr;
+
+            // First make sure our pointer is aligned on a word boundary
+            int alignment = IntPtr.Size - 1;
+
+            // If ptr is at an odd address (e.g. 0x5), this loop will simply iterate all the way
+            while (((uint)end & (uint)alignment) != 0)
+            {
+                if (*end == 0) goto FoundZero;
+                end++;
+            }
+
+#if !BIT64
+            // The following code is (somewhat surprisingly!) significantly faster than a naive loop,
+            // at least on x86 and the current jit.
+
+            // The loop condition below works because if "end[0] & end[1]" is non-zero, that means
+            // neither operand can have been zero. If is zero, we have to look at the operands individually,
+            // but we hope this going to fairly rare.
+
+            // In general, it would be incorrect to access end[1] if we haven't made sure
+            // end[0] is non-zero. However, we know the ptr has been aligned by the loop above
+            // so end[0] and end[1] must be in the same word (and therefore page), so they're either both accessible, or both not.
+
+            while ((end[0] & end[1]) != 0 || (end[0] != 0 && end[1] != 0))
+            {
+                end += 2;
+            }
+
+            Debug.Assert(end[0] == 0 || end[1] == 0);
+            if (end[0] != 0) end++;
+#else // !BIT64
+            // Based on https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
+
+            // 64-bit implementation: process 1 ulong (word) at a time
+
+            // What we do here is add 0x7fff from each of the
+            // 4 individual chars within the ulong, using MagicMask.
+            // If the char > 0 and < 0x8001, it will have its high bit set.
+            // We then OR with MagicMask, to set all the other bits.
+            // This will result in all bits set (ulong.MaxValue) for any
+            // char that fits the above criteria, and something else otherwise.
+
+            // Note that for any char > 0x8000, this will be a false
+            // positive and we will fallback to the slow path and
+            // check each char individually. This is OK though, since
+            // we optimize for the common case (ASCII chars, which are < 0x80).
+
+            // NOTE: We can access a ulong a time since the ptr is aligned,
+            // and therefore we're only accessing the same word/page. (See notes
+            // for the 32-bit version above.)
+
+            const ulong MagicMask = 0x7fff7fff7fff7fff;
+
+            while (true)
+            {
+                ulong word = *(ulong*)end;
+                word += MagicMask; // cause high bit to be set if not zero, and <= 0x8000
+                word |= MagicMask; // set everything besides the high bits
+
+                if (word == ulong.MaxValue) // 0xffff...
+                {
+                    // all of the chars have their bits set (and therefore none can be 0)
+                    end += 4;
+                    continue;
+                }
+
+                // at least one of them didn't have their high bit set!
+                // go through each char and check for 0.
+
+                if (end[0] == 0) goto EndAt0;
+                if (end[1] == 0) goto EndAt1;
+                if (end[2] == 0) goto EndAt2;
+                if (end[3] == 0) goto EndAt3;
+
+                // if we reached here, it was a false positive-- just continue
+                end += 4;
+            }
+
+            EndAt3: end++;
+            EndAt2: end++;
+            EndAt1: end++;
+            EndAt0:
+#endif // !BIT64
+
+            FoundZero:
+            Debug.Assert(*end == 0);
+
+            int count = (int)(end - ptr);
+
+#if BIT64
+            // Check for overflow
+            if (ptr + count != end)
+                throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
+#else
+            Debug.Assert(ptr + count == end);
+#endif
+
+            return count;
+        }
+
+        //
+        // IConvertible implementation
+        // 
+
+        public TypeCode GetTypeCode()
+        {
+            return TypeCode.String;
+        }
+
+        bool IConvertible.ToBoolean(IFormatProvider provider)
+        {
+            return Convert.ToBoolean(this, provider);
+        }
+
+        char IConvertible.ToChar(IFormatProvider provider)
+        {
+            return Convert.ToChar(this, provider);
+        }
+
+        sbyte IConvertible.ToSByte(IFormatProvider provider)
+        {
+            return Convert.ToSByte(this, provider);
+        }
+
+        byte IConvertible.ToByte(IFormatProvider provider)
+        {
+            return Convert.ToByte(this, provider);
+        }
+
+        short IConvertible.ToInt16(IFormatProvider provider)
+        {
+            return Convert.ToInt16(this, provider);
+        }
+
+        ushort IConvertible.ToUInt16(IFormatProvider provider)
+        {
+            return Convert.ToUInt16(this, provider);
+        }
+
+        int IConvertible.ToInt32(IFormatProvider provider)
+        {
+            return Convert.ToInt32(this, provider);
+        }
+
+        uint IConvertible.ToUInt32(IFormatProvider provider)
+        {
+            return Convert.ToUInt32(this, provider);
+        }
+
+        long IConvertible.ToInt64(IFormatProvider provider)
+        {
+            return Convert.ToInt64(this, provider);
+        }
+
+        ulong IConvertible.ToUInt64(IFormatProvider provider)
+        {
+            return Convert.ToUInt64(this, provider);
+        }
+
+        float IConvertible.ToSingle(IFormatProvider provider)
+        {
+            return Convert.ToSingle(this, provider);
+        }
+
+        double IConvertible.ToDouble(IFormatProvider provider)
+        {
+            return Convert.ToDouble(this, provider);
+        }
+
+        Decimal IConvertible.ToDecimal(IFormatProvider provider)
+        {
+            return Convert.ToDecimal(this, provider);
+        }
+
+        DateTime IConvertible.ToDateTime(IFormatProvider provider)
+        {
+            return Convert.ToDateTime(this, provider);
+        }
+
+        Object IConvertible.ToType(Type type, IFormatProvider provider)
+        {
+            return Convert.DefaultToType((IConvertible)this, type, provider);
+        }
+
+        // Normalization Methods
+        // These just wrap calls to Normalization class
+        public bool IsNormalized()
+        {
+            return IsNormalized(NormalizationForm.FormC);
+        }
+
+        public bool IsNormalized(NormalizationForm normalizationForm)
+        {
+#if CORECLR
+            if (this.IsFastSort())
+            {
+                // If its FastSort && one of the 4 main forms, then its already normalized
+                if (normalizationForm == NormalizationForm.FormC ||
+                    normalizationForm == NormalizationForm.FormKC ||
+                    normalizationForm == NormalizationForm.FormD ||
+                    normalizationForm == NormalizationForm.FormKD)
+                    return true;
+            }
+#endif
+            return Normalization.IsNormalized(this, normalizationForm);
+        }
+
+        public string Normalize()
+        {
+            return Normalize(NormalizationForm.FormC);
+        }
+
+        public string Normalize(NormalizationForm normalizationForm)
+        {
+#if CORECLR
+            if (this.IsAscii())
+            {
+                // If its FastSort && one of the 4 main forms, then its already normalized
+                if (normalizationForm == NormalizationForm.FormC ||
+                    normalizationForm == NormalizationForm.FormKC ||
+                    normalizationForm == NormalizationForm.FormD ||
+                    normalizationForm == NormalizationForm.FormKD)
+                    return this;
+            }
+#endif
+            return Normalization.Normalize(this, normalizationForm);
+        }
+    }
+}
index 185e612..d318959 100644 (file)
@@ -38,82 +38,6 @@ namespace System
         public static extern void BlockCopy(Array src, int srcOffset,
             Array dst, int dstOffset, int count);
 
-        // This is ported from the optimized CRT assembly in memchr.asm. The JIT generates 
-        // pretty good code here and this ends up being within a couple % of the CRT asm.
-        // It is however cross platform as the CRT hasn't ported their fast version to 64-bit
-        // platforms.
-        //
-        internal unsafe static int IndexOfByte(byte* src, byte value, int index, int count)
-        {
-            Debug.Assert(src != null, "src should not be null");
-
-            byte* pByte = src + index;
-
-            // Align up the pointer to sizeof(int).
-            while (((int)pByte & 3) != 0)
-            {
-                if (count == 0)
-                    return -1;
-                else if (*pByte == value)
-                    return (int)(pByte - src);
-
-                count--;
-                pByte++;
-            }
-
-            // Fill comparer with value byte for comparisons
-            //
-            // comparer = 0/0/value/value
-            uint comparer = (((uint)value << 8) + (uint)value);
-            // comparer = value/value/value/value
-            comparer = (comparer << 16) + comparer;
-
-            // Run through buffer until we hit a 4-byte section which contains
-            // the byte we're looking for or until we exhaust the buffer.
-            while (count > 3)
-            {
-                // Test the buffer for presence of value. comparer contains the byte
-                // replicated 4 times.
-                uint t1 = *(uint*)pByte;
-                t1 = t1 ^ comparer;
-                uint t2 = 0x7efefeff + t1;
-                t1 = t1 ^ 0xffffffff;
-                t1 = t1 ^ t2;
-                t1 = t1 & 0x81010100;
-
-                // if t1 is zero then these 4-bytes don't contain a match
-                if (t1 != 0)
-                {
-                    // We've found a match for value, figure out which position it's in.
-                    int foundIndex = (int)(pByte - src);
-                    if (pByte[0] == value)
-                        return foundIndex;
-                    else if (pByte[1] == value)
-                        return foundIndex + 1;
-                    else if (pByte[2] == value)
-                        return foundIndex + 2;
-                    else if (pByte[3] == value)
-                        return foundIndex + 3;
-                }
-
-                count -= 4;
-                pByte += 4;
-            }
-
-            // Catch any bytes that might be left at the tail of the buffer
-            while (count > 0)
-            {
-                if (*pByte == value)
-                    return (int)(pByte - src);
-
-                count--;
-                pByte++;
-            }
-
-            // If we don't have a match return -1;
-            return -1;
-        }
-
         // Returns a bool to indicate if the array is of primitive data types
         // or not.
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
index 57b63eb..567a22d 100644 (file)
@@ -289,29 +289,15 @@ namespace System.Collections.Generic
 
         internal unsafe override int IndexOf(byte[] array, byte value, int startIndex, int count)
         {
-            if (array == null)
-                throw new ArgumentNullException(nameof(array));
-            if (startIndex < 0)
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
-            if (count < 0)
-                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
-            if (count > array.Length - startIndex)
-                throw new ArgumentException(SR.Argument_InvalidOffLen);
-            if (count == 0) return -1;
-            fixed (byte* pbytes = array)
-            {
-                return Buffer.IndexOfByte(pbytes, value, startIndex, count);
-            }
+            int found = new ReadOnlySpan<byte>(array, startIndex, count).IndexOf(value);
+            return (found >= 0) ? (startIndex + found) : found;
         }
 
         internal override int LastIndexOf(byte[] array, byte value, int startIndex, int count)
         {
             int endIndex = startIndex - count + 1;
-            for (int i = startIndex; i >= endIndex; i--)
-            {
-                if (array[i] == value) return i;
-            }
-            return -1;
+            int found = new ReadOnlySpan<byte>(array, endIndex, count).LastIndexOf(value);
+            return (found >= 0) ? (endIndex + found) : found;
         }
 
         // Equals method for the comparer itself.
index cfaf13f..70b9e38 100644 (file)
@@ -323,7 +323,7 @@ namespace System
                     T[] list = null;
 
                     if (name == null || name.Length == 0 ||
-                        (cacheType == CacheType.Constructor && name.FirstChar != '.' && name.FirstChar != '*'))
+                        (cacheType == CacheType.Constructor && name[0] != '.' && name[0] != '*'))
                     {
                         list = GetListByName(null, 0, null, 0, listType, cacheType);
                     }
diff --git a/src/mscorlib/src/System/String.CoreCLR.cs b/src/mscorlib/src/System/String.CoreCLR.cs
new file mode 100644 (file)
index 0000000..7685af2
--- /dev/null
@@ -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.
+
+using Microsoft.Win32;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace System
+{
+    public partial class String
+    {
+        //
+        // These fields map directly onto the fields in an EE StringObject.  See object.h for the layout.
+        //
+        [NonSerialized] private int _stringLength;
+
+        // For empty strings, this will be '\0' since
+        // strings are both null-terminated and length prefixed
+        [NonSerialized] private char _firstChar;
+
+        // The Empty constant holds the empty string value. It is initialized by the EE during startup.
+        // It is treated as intrinsic by the JIT as so the static constructor would never run.
+        // Leaving it uninitialized would confuse debuggers.
+        //
+        // We need to call the String constructor so that the compiler doesn't mark this as a literal.
+        // Marking this as a literal would mean that it doesn't show up as a field which we can access 
+        // from native.
+        public static readonly String Empty;
+
+        // Gets the character at a specified position.
+        //
+        [System.Runtime.CompilerServices.IndexerName("Chars")]
+        public extern char this[int index]
+        {
+            [MethodImpl(MethodImplOptions.InternalCall)]
+            get;
+        }
+
+        // Gets the length of this string
+        //
+        // This is a EE implemented function so that the JIT can recognise it specially
+        // and eliminate checks on character fetches in a loop like:
+        //        for(int i = 0; i < str.Length; i++) str[i]
+        // The actual code generated for this will be one instruction and will be inlined.
+        //
+        public extern int Length
+        {
+            [MethodImplAttribute(MethodImplOptions.InternalCall)]
+            get;
+        }
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal extern static String FastAllocateString(int length);
+
+        // Is this a string that can be compared quickly (that is it has only characters > 0x80 
+        // and not a - or '
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal extern bool IsFastSort();
+        // Is this a string that only contains characters < 0x80.
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal extern bool IsAscii();
+
+#if FEATURE_COMINTEROP
+        // Set extra byte for odd-sized strings that came from interop as BSTR.
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal extern void SetTrailByte(byte data);
+        // Try to retrieve the extra byte - returns false if not present.
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal extern bool TryGetTrailByte(out byte data);
+#endif
+
+        public static String Intern(String str)
+        {
+            if (str == null)
+            {
+                throw new ArgumentNullException(nameof(str));
+            }
+
+            return Thread.GetDomain().GetOrInternString(str);
+        }
+
+        public static String IsInterned(String str)
+        {
+            if (str == null)
+            {
+                throw new ArgumentNullException(nameof(str));
+            }
+
+            return Thread.GetDomain().IsStringInterned(str);
+        }
+
+        // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes.
+        internal unsafe static void InternalCopy(String src, IntPtr dest, int len)
+        {
+            if (len == 0)
+                return;
+            fixed (char* charPtr = &src._firstChar)
+            {
+                byte* srcPtr = (byte*)charPtr;
+                byte* dstPtr = (byte*)dest;
+                Buffer.Memcpy(dstPtr, srcPtr, len);
+            }
+        }
+
+        internal unsafe int GetBytesFromEncoding(byte* pbNativeBuffer, int cbNativeBuffer, Encoding encoding)
+        {
+            // encoding == Encoding.UTF8
+            fixed (char* pwzChar = &_firstChar)
+            {
+                return encoding.GetBytes(pwzChar, Length, pbNativeBuffer, cbNativeBuffer);
+            }
+        }
+
+        internal unsafe int ConvertToAnsi(byte* pbNativeBuffer, int cbNativeBuffer, bool fBestFit, bool fThrowOnUnmappableChar)
+        {
+            Debug.Assert(cbNativeBuffer >= (Length + 1) * Marshal.SystemMaxDBCSCharSize, "Insufficient buffer length passed to ConvertToAnsi");
+
+            const uint CP_ACP = 0;
+            int nb;
+
+            const uint WC_NO_BEST_FIT_CHARS = 0x00000400;
+
+            uint flgs = (fBestFit ? 0 : WC_NO_BEST_FIT_CHARS);
+            uint DefaultCharUsed = 0;
+
+            fixed (char* pwzChar = &_firstChar)
+            {
+                nb = Win32Native.WideCharToMultiByte(
+                    CP_ACP,
+                    flgs,
+                    pwzChar,
+                    this.Length,
+                    pbNativeBuffer,
+                    cbNativeBuffer,
+                    IntPtr.Zero,
+                    (fThrowOnUnmappableChar ? new IntPtr(&DefaultCharUsed) : IntPtr.Zero));
+            }
+
+            if (0 != DefaultCharUsed)
+            {
+                throw new ArgumentException(SR.Interop_Marshal_Unmappable_Char);
+            }
+
+            pbNativeBuffer[nb] = 0;
+            return nb;
+        }
+    }
+}
diff --git a/src/mscorlib/src/System/String.cs b/src/mscorlib/src/System/String.cs
deleted file mode 100644 (file)
index 6974cde..0000000
+++ /dev/null
@@ -1,896 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose: Your favorite String class.  Native methods 
-** are implemented in StringNative.cpp
-**
-**
-===========================================================*/
-
-namespace System
-{
-    using System.Text;
-    using System;
-    using System.Buffers;
-    using System.Runtime;
-    using System.Runtime.ConstrainedExecution;
-    using System.Globalization;
-    using System.Threading;
-    using System.Collections;
-    using System.Collections.Generic;
-    using System.Runtime.CompilerServices;
-    using System.Runtime.InteropServices;
-    using System.Runtime.Versioning;
-    using Microsoft.Win32;
-    using System.Diagnostics;
-    using System.Security;
-
-    //
-    // For Information on these methods, please see COMString.cpp
-    //
-    // The String class represents a static string of characters.  Many of
-    // the String methods perform some type of transformation on the current
-    // instance and return the result as a new String. All comparison methods are
-    // implemented as a part of String.  As with arrays, character positions
-    // (indices) are zero-based.
-
-    [Serializable]
-    [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
-    public sealed partial class String : IComparable, ICloneable, IConvertible, IEnumerable
-        , IComparable<String>, IEnumerable<char>, IEquatable<String>
-    {
-        //
-        //NOTE NOTE NOTE NOTE
-        //These fields map directly onto the fields in an EE StringObject.  See object.h for the layout.
-        //
-        [NonSerialized] private int m_stringLength;
-
-        // For empty strings, this will be '\0' since
-        // strings are both null-terminated and length prefixed
-        [NonSerialized] private char _firstChar;
-
-        // The Empty constant holds the empty string value. It is initialized by the EE during startup.
-        // It is treated as intrinsic by the JIT as so the static constructor would never run.
-        // Leaving it uninitialized would confuse debuggers.
-        //
-        //We need to call the String constructor so that the compiler doesn't mark this as a literal.
-        //Marking this as a literal would mean that it doesn't show up as a field which we can access 
-        //from native.
-        public static readonly String Empty;
-
-        internal char FirstChar { get { return _firstChar; } }
-        //
-        // This is a helper method for the security team.  They need to uppercase some strings (guaranteed to be less 
-        // than 0x80) before security is fully initialized.  Without security initialized, we can't grab resources (the nlp's)
-        // from the assembly.  This provides a workaround for that problem and should NOT be used anywhere else.
-        //
-        internal unsafe static string SmallCharToUpper(string strIn)
-        {
-            Debug.Assert(strIn != null);
-            //
-            // Get the length and pointers to each of the buffers.  Walk the length
-            // of the string and copy the characters from the inBuffer to the outBuffer,
-            // capitalizing it if necessary.  We assert that all of our characters are
-            // less than 0x80.
-            //
-            int length = strIn.Length;
-            String strOut = FastAllocateString(length);
-            fixed (char* inBuff = &strIn._firstChar, outBuff = &strOut._firstChar)
-            {
-                for (int i = 0; i < length; i++)
-                {
-                    int c = inBuff[i];
-                    Debug.Assert(c <= 0x7F, "string has to be ASCII");
-
-                    // uppercase - notice that we need just one compare
-                    if ((uint)(c - 'a') <= (uint)('z' - 'a')) c -= 0x20;
-
-                    outBuff[i] = (char)c;
-                }
-
-                Debug.Assert(outBuff[length] == '\0', "outBuff[length]=='\0'");
-            }
-            return strOut;
-        }
-
-        // Gets the character at a specified position.
-        //
-        [System.Runtime.CompilerServices.IndexerName("Chars")]
-        public extern char this[int index]
-        {
-            [MethodImpl(MethodImplOptions.InternalCall)]
-            get;
-        }
-
-        // Converts a substring of this string to an array of characters.  Copies the
-        // characters of this string beginning at position sourceIndex and ending at
-        // sourceIndex + count - 1 to the character array buffer, beginning
-        // at destinationIndex.
-        //
-        unsafe public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
-        {
-            if (destination == null)
-                throw new ArgumentNullException(nameof(destination));
-            if (count < 0)
-                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
-            if (sourceIndex < 0)
-                throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
-            if (count > Length - sourceIndex)
-                throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_IndexCount);
-            if (destinationIndex > destination.Length - count || destinationIndex < 0)
-                throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_IndexCount);
-
-            // Note: fixed does not like empty arrays
-            if (count > 0)
-            {
-                fixed (char* src = &_firstChar)
-                fixed (char* dest = destination)
-                    wstrcpy(dest + destinationIndex, src + sourceIndex, count);
-            }
-        }
-
-        // Returns the entire string as an array of characters.
-        unsafe public char[] ToCharArray()
-        {
-            int length = Length;
-            if (length > 0)
-            {
-                char[] chars = new char[length];
-                fixed (char* src = &_firstChar) fixed (char* dest = chars)
-                {
-                    wstrcpy(dest, src, length);
-                }
-                return chars;
-            }
-
-            return Array.Empty<char>();
-        }
-
-        // Returns a substring of this string as an array of characters.
-        //
-        unsafe public char[] ToCharArray(int startIndex, int length)
-        {
-            // Range check everything.
-            if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
-            if (length < 0)
-                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
-
-            if (length > 0)
-            {
-                char[] chars = new char[length];
-                fixed (char* src = &_firstChar) fixed (char* dest = chars)
-                {
-                    wstrcpy(dest, src + startIndex, length);
-                }
-                return chars;
-            }
-
-            return Array.Empty<char>();
-        }
-
-        public static bool IsNullOrEmpty(String value)
-        {
-            return (value == null || value.Length == 0);
-        }
-
-        public static bool IsNullOrWhiteSpace(String value)
-        {
-            if (value == null) return true;
-
-            for (int i = 0; i < value.Length; i++)
-            {
-                if (!Char.IsWhiteSpace(value[i])) return false;
-            }
-
-            return true;
-        }
-
-        // Gets the length of this string
-        //
-        /// This is a EE implemented function so that the JIT can recognise is specially
-        /// and eliminate checks on character fetchs in a loop like:
-        ///        for(int i = 0; i < str.Length; i++) str[i]
-        /// The actually code generated for this will be one instruction and will be inlined.
-        //
-        public extern int Length
-        {
-            [MethodImplAttribute(MethodImplOptions.InternalCall)]
-            get;
-        }
-
-        // Creates a new string with the characters copied in from ptr. If
-        // ptr is null, a 0-length string (like String.Empty) is returned.
-        //
-        [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
-        unsafe public extern String(char* value);
-        [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
-        unsafe public extern String(char* value, int startIndex, int length);
-
-        [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
-        unsafe public extern String(sbyte* value);
-        [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
-        unsafe public extern String(sbyte* value, int startIndex, int length);
-
-        [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
-        unsafe public extern String(sbyte* value, int startIndex, int length, Encoding enc);
-
-        unsafe static private String CreateString(sbyte* value, int startIndex, int length, Encoding enc)
-        {
-            if (enc == null)
-                return new String(value, startIndex, length); // default to ANSI
-
-            if (length < 0)
-                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
-            if (startIndex < 0)
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
-            if ((value + startIndex) < value)
-            {
-                // overflow check
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
-            }
-
-            byte[] b = new byte[length];
-
-            try
-            {
-                Buffer.Memcpy(b, 0, (byte*)value, startIndex, length);
-            }
-            catch (NullReferenceException)
-            {
-                // If we got a NullReferencException. It means the pointer or 
-                // the index is out of range
-                throw new ArgumentOutOfRangeException(nameof(value),
-                        SR.ArgumentOutOfRange_PartialWCHAR);
-            }
-
-            return enc.GetString(b);
-        }
-
-        // Helper for encodings so they can talk to our buffer directly
-        // stringLength must be the exact size we'll expect
-        unsafe static internal String CreateStringFromEncoding(
-            byte* bytes, int byteLength, Encoding encoding)
-        {
-            Debug.Assert(bytes != null);
-            Debug.Assert(byteLength >= 0);
-
-            // Get our string length
-            int stringLength = encoding.GetCharCount(bytes, byteLength, null);
-            Debug.Assert(stringLength >= 0, "stringLength >= 0");
-
-            // They gave us an empty string if they needed one
-            // 0 bytelength might be possible if there's something in an encoder
-            if (stringLength == 0)
-                return String.Empty;
-
-            String s = FastAllocateString(stringLength);
-            fixed (char* pTempChars = &s._firstChar)
-            {
-                int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
-                Debug.Assert(stringLength == doubleCheck,
-                    "Expected encoding.GetChars to return same length as encoding.GetCharCount");
-            }
-
-            return s;
-        }
-
-        // This is only intended to be used by char.ToString.
-        // It is necessary to put the code in this class instead of Char, since _firstChar is a private member.
-        // Making _firstChar internal would be dangerous since it would make it much easier to break String's immutability.
-        internal static string CreateFromChar(char c)
-        {
-            string result = FastAllocateString(1);
-            result._firstChar = c;
-            return result;
-        }
-
-        unsafe internal int GetBytesFromEncoding(byte* pbNativeBuffer, int cbNativeBuffer, Encoding encoding)
-        {
-            // encoding == Encoding.UTF8
-            fixed (char* pwzChar = &_firstChar)
-            {
-                return encoding.GetBytes(pwzChar, m_stringLength, pbNativeBuffer, cbNativeBuffer);
-            }
-        }
-
-        unsafe internal int ConvertToAnsi(byte* pbNativeBuffer, int cbNativeBuffer, bool fBestFit, bool fThrowOnUnmappableChar)
-        {
-            Debug.Assert(cbNativeBuffer >= (Length + 1) * Marshal.SystemMaxDBCSCharSize, "Insufficient buffer length passed to ConvertToAnsi");
-
-            const uint CP_ACP = 0;
-            int nb;
-
-            const uint WC_NO_BEST_FIT_CHARS = 0x00000400;
-
-            uint flgs = (fBestFit ? 0 : WC_NO_BEST_FIT_CHARS);
-            uint DefaultCharUsed = 0;
-
-            fixed (char* pwzChar = &_firstChar)
-            {
-                nb = Win32Native.WideCharToMultiByte(
-                    CP_ACP,
-                    flgs,
-                    pwzChar,
-                    this.Length,
-                    pbNativeBuffer,
-                    cbNativeBuffer,
-                    IntPtr.Zero,
-                    (fThrowOnUnmappableChar ? new IntPtr(&DefaultCharUsed) : IntPtr.Zero));
-            }
-
-            if (0 != DefaultCharUsed)
-            {
-                throw new ArgumentException(SR.Interop_Marshal_Unmappable_Char);
-            }
-
-            pbNativeBuffer[nb] = 0;
-            return nb;
-        }
-
-        // Normalization Methods
-        // These just wrap calls to Normalization class
-        public bool IsNormalized()
-        {
-            // Default to Form C
-            return IsNormalized(NormalizationForm.FormC);
-        }
-
-        public bool IsNormalized(NormalizationForm normalizationForm)
-        {
-            if (this.IsFastSort())
-            {
-                // If its FastSort && one of the 4 main forms, then its already normalized
-                if (normalizationForm == NormalizationForm.FormC ||
-                    normalizationForm == NormalizationForm.FormKC ||
-                    normalizationForm == NormalizationForm.FormD ||
-                    normalizationForm == NormalizationForm.FormKD)
-                    return true;
-            }
-            return Normalization.IsNormalized(this, normalizationForm);
-        }
-
-        public String Normalize()
-        {
-            // Default to Form C
-            return Normalize(NormalizationForm.FormC);
-        }
-
-        public String Normalize(NormalizationForm normalizationForm)
-        {
-            if (this.IsAscii())
-            {
-                // If its FastSort && one of the 4 main forms, then its already normalized
-                if (normalizationForm == NormalizationForm.FormC ||
-                    normalizationForm == NormalizationForm.FormKC ||
-                    normalizationForm == NormalizationForm.FormD ||
-                    normalizationForm == NormalizationForm.FormKD)
-                    return this;
-            }
-            return Normalization.Normalize(this, normalizationForm);
-        }
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal extern static String FastAllocateString(int length);
-
-        // Creates a new string from the characters in a subarray.  The new string will
-        // be created from the characters in value between startIndex and
-        // startIndex + length - 1.
-        //
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern String(char[] value, int startIndex, int length);
-
-        // Creates a new string from the characters in a subarray.  The new string will be
-        // created from the characters in value.
-        //
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern String(char[] value);
-
-        internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
-        {
-            Buffer.Memcpy((byte*)dmem, (byte*)smem, charCount * 2); // 2 used everywhere instead of sizeof(char)
-        }
-
-        private String CtorCharArray(char[] value)
-        {
-            if (value != null && value.Length != 0)
-            {
-                String result = FastAllocateString(value.Length);
-
-                unsafe
-                {
-                    fixed (char* dest = &result._firstChar, source = value)
-                    {
-                        wstrcpy(dest, source, value.Length);
-                    }
-                }
-                return result;
-            }
-            else
-                return String.Empty;
-        }
-
-        private String CtorCharArrayStartLength(char[] value, int startIndex, int length)
-        {
-            if (value == null)
-                throw new ArgumentNullException(nameof(value));
-
-            if (startIndex < 0)
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
-
-            if (length < 0)
-                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
-
-            if (startIndex > value.Length - length)
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
-
-            if (length > 0)
-            {
-                String result = FastAllocateString(length);
-
-                unsafe
-                {
-                    fixed (char* dest = &result._firstChar, source = value)
-                    {
-                        wstrcpy(dest, source + startIndex, length);
-                    }
-                }
-                return result;
-            }
-            else
-                return String.Empty;
-        }
-
-        private String CtorCharCount(char c, int count)
-        {
-            if (count > 0)
-            {
-                String result = FastAllocateString(count);
-                if (c != 0)
-                {
-                    unsafe
-                    {
-                        fixed (char* dest = &result._firstChar)
-                        {
-                            char* dmem = dest;
-                            while (((uint)dmem & 3) != 0 && count > 0)
-                            {
-                                *dmem++ = c;
-                                count--;
-                            }
-                            uint cc = (uint)((c << 16) | c);
-                            if (count >= 4)
-                            {
-                                count -= 4;
-                                do
-                                {
-                                    ((uint*)dmem)[0] = cc;
-                                    ((uint*)dmem)[1] = cc;
-                                    dmem += 4;
-                                    count -= 4;
-                                } while (count >= 0);
-                            }
-                            if ((count & 2) != 0)
-                            {
-                                ((uint*)dmem)[0] = cc;
-                                dmem += 2;
-                            }
-                            if ((count & 1) != 0)
-                                dmem[0] = c;
-                        }
-                    }
-                }
-                return result;
-            }
-            else if (count == 0)
-                return String.Empty;
-            else
-                throw new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(count)));
-        }
-
-        internal static unsafe int wcslen(char* ptr)
-        {
-            char* end = ptr;
-
-            // First make sure our pointer is aligned on a word boundary
-            int alignment = IntPtr.Size - 1;
-
-            // If ptr is at an odd address (e.g. 0x5), this loop will simply iterate all the way
-            while (((uint)end & (uint)alignment) != 0)
-            {
-                if (*end == 0) goto FoundZero;
-                end++;
-            }
-
-#if !BIT64
-            // The following code is (somewhat surprisingly!) significantly faster than a naive loop,
-            // at least on x86 and the current jit.
-
-            // The loop condition below works because if "end[0] & end[1]" is non-zero, that means
-            // neither operand can have been zero. If is zero, we have to look at the operands individually,
-            // but we hope this going to fairly rare.
-
-            // In general, it would be incorrect to access end[1] if we haven't made sure
-            // end[0] is non-zero. However, we know the ptr has been aligned by the loop above
-            // so end[0] and end[1] must be in the same word (and therefore page), so they're either both accessible, or both not.
-
-            while ((end[0] & end[1]) != 0 || (end[0] != 0 && end[1] != 0)) {
-                end += 2;
-            }
-
-            Debug.Assert(end[0] == 0 || end[1] == 0);
-            if (end[0] != 0) end++;
-#else // !BIT64
-            // Based on https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
-
-            // 64-bit implementation: process 1 ulong (word) at a time
-
-            // What we do here is add 0x7fff from each of the
-            // 4 individual chars within the ulong, using MagicMask.
-            // If the char > 0 and < 0x8001, it will have its high bit set.
-            // We then OR with MagicMask, to set all the other bits.
-            // This will result in all bits set (ulong.MaxValue) for any
-            // char that fits the above criteria, and something else otherwise.
-
-            // Note that for any char > 0x8000, this will be a false
-            // positive and we will fallback to the slow path and
-            // check each char individually. This is OK though, since
-            // we optimize for the common case (ASCII chars, which are < 0x80).
-
-            // NOTE: We can access a ulong a time since the ptr is aligned,
-            // and therefore we're only accessing the same word/page. (See notes
-            // for the 32-bit version above.)
-
-            const ulong MagicMask = 0x7fff7fff7fff7fff;
-
-            while (true)
-            {
-                ulong word = *(ulong*)end;
-                word += MagicMask; // cause high bit to be set if not zero, and <= 0x8000
-                word |= MagicMask; // set everything besides the high bits
-
-                if (word == ulong.MaxValue) // 0xffff...
-                {
-                    // all of the chars have their bits set (and therefore none can be 0)
-                    end += 4;
-                    continue;
-                }
-
-                // at least one of them didn't have their high bit set!
-                // go through each char and check for 0.
-
-                if (end[0] == 0) goto EndAt0;
-                if (end[1] == 0) goto EndAt1;
-                if (end[2] == 0) goto EndAt2;
-                if (end[3] == 0) goto EndAt3;
-
-                // if we reached here, it was a false positive-- just continue
-                end += 4;
-            }
-
-        EndAt3: end++;
-        EndAt2: end++;
-        EndAt1: end++;
-        EndAt0:
-#endif // !BIT64
-
-        FoundZero:
-            Debug.Assert(*end == 0);
-
-            int count = (int)(end - ptr);
-
-            return count;
-        }
-
-        private unsafe String CtorCharPtr(char* ptr)
-        {
-            if (ptr == null)
-                return String.Empty;
-
-#if !FEATURE_PAL
-            if (ptr < (char*)64000)
-                throw new ArgumentException(SR.Arg_MustBeStringPtrNotAtom);
-#endif // FEATURE_PAL
-
-            Debug.Assert(this == null, "this == null");        // this is the string constructor, we allocate it
-
-            try
-            {
-                int count = wcslen(ptr);
-                if (count == 0)
-                    return String.Empty;
-
-                String result = FastAllocateString(count);
-                fixed (char* dest = &result._firstChar)
-                    wstrcpy(dest, ptr, count);
-                return result;
-            }
-            catch (NullReferenceException)
-            {
-                throw new ArgumentOutOfRangeException(nameof(ptr), SR.ArgumentOutOfRange_PartialWCHAR);
-            }
-        }
-
-        private unsafe String CtorCharPtrStartLength(char* ptr, int startIndex, int length)
-        {
-            if (length < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
-            }
-
-            if (startIndex < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
-            }
-            Debug.Assert(this == null, "this == null");        // this is the string constructor, we allocate it
-
-            char* pFrom = ptr + startIndex;
-            if (pFrom < ptr)
-            {
-                // This means that the pointer operation has had an overflow
-                throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
-            }
-
-            if (length == 0)
-                return String.Empty;
-
-            String result = FastAllocateString(length);
-
-            try
-            {
-                fixed (char* dest = &result._firstChar)
-                    wstrcpy(dest, pFrom, length);
-                return result;
-            }
-            catch (NullReferenceException)
-            {
-                throw new ArgumentOutOfRangeException(nameof(ptr), SR.ArgumentOutOfRange_PartialWCHAR);
-            }
-        }
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern String(char c, int count);
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern String(ReadOnlySpan<char> value);
-
-        private unsafe string CtorReadOnlySpanOfChar(ReadOnlySpan<char> value)
-        {
-            if (value.Length == 0)
-            {
-                return Empty;
-            }
-
-            string result = FastAllocateString(value.Length);
-            fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value))
-            {
-                wstrcpy(dest, src, value.Length);
-            }
-            return result;
-        }
-
-        public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
-        {
-            if (action == null)
-            {
-                throw new ArgumentNullException(nameof(action));
-            }
-
-            if (length > 0)
-            {
-                string result = FastAllocateString(length);
-                action(new Span<char>(ref result.GetRawStringData(), length), state);
-                return result;
-            }
-
-            if (length == 0)
-            {
-                return Empty;
-            }
-
-            throw new ArgumentOutOfRangeException(nameof(length));
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static implicit operator ReadOnlySpan<char>(string value) =>
-            value != null ? new ReadOnlySpan<char>(ref value.GetRawStringData(), value.Length) : default;
-
-        // Returns this string.
-        public override String ToString()
-        {
-            return this;
-        }
-
-        public String ToString(IFormatProvider provider)
-        {
-            return this;
-        }
-
-        // Method required for the ICloneable interface.
-        // There's no point in cloning a string since they're immutable, so we simply return this.
-        public Object Clone()
-        {
-            return this;
-        }
-
-        unsafe public static String Copy(String str)
-        {
-            if (str == null)
-            {
-                throw new ArgumentNullException(nameof(str));
-            }
-
-            int length = str.Length;
-
-            String result = FastAllocateString(length);
-
-            fixed (char* dest = &result._firstChar)
-            fixed (char* src = &str._firstChar)
-            {
-                wstrcpy(dest, src, length);
-            }
-            return result;
-        }
-
-        public static String Intern(String str)
-        {
-            if (str == null)
-            {
-                throw new ArgumentNullException(nameof(str));
-            }
-
-            return Thread.GetDomain().GetOrInternString(str);
-        }
-
-        public static String IsInterned(String str)
-        {
-            if (str == null)
-            {
-                throw new ArgumentNullException(nameof(str));
-            }
-
-            return Thread.GetDomain().IsStringInterned(str);
-        }
-
-
-        //
-        // IConvertible implementation
-        // 
-
-        public TypeCode GetTypeCode()
-        {
-            return TypeCode.String;
-        }
-
-        bool IConvertible.ToBoolean(IFormatProvider provider)
-        {
-            return Convert.ToBoolean(this, provider);
-        }
-
-        char IConvertible.ToChar(IFormatProvider provider)
-        {
-            return Convert.ToChar(this, provider);
-        }
-
-        sbyte IConvertible.ToSByte(IFormatProvider provider)
-        {
-            return Convert.ToSByte(this, provider);
-        }
-
-        byte IConvertible.ToByte(IFormatProvider provider)
-        {
-            return Convert.ToByte(this, provider);
-        }
-
-        short IConvertible.ToInt16(IFormatProvider provider)
-        {
-            return Convert.ToInt16(this, provider);
-        }
-
-        ushort IConvertible.ToUInt16(IFormatProvider provider)
-        {
-            return Convert.ToUInt16(this, provider);
-        }
-
-        int IConvertible.ToInt32(IFormatProvider provider)
-        {
-            return Convert.ToInt32(this, provider);
-        }
-
-        uint IConvertible.ToUInt32(IFormatProvider provider)
-        {
-            return Convert.ToUInt32(this, provider);
-        }
-
-        long IConvertible.ToInt64(IFormatProvider provider)
-        {
-            return Convert.ToInt64(this, provider);
-        }
-
-        ulong IConvertible.ToUInt64(IFormatProvider provider)
-        {
-            return Convert.ToUInt64(this, provider);
-        }
-
-        float IConvertible.ToSingle(IFormatProvider provider)
-        {
-            return Convert.ToSingle(this, provider);
-        }
-
-        double IConvertible.ToDouble(IFormatProvider provider)
-        {
-            return Convert.ToDouble(this, provider);
-        }
-
-        Decimal IConvertible.ToDecimal(IFormatProvider provider)
-        {
-            return Convert.ToDecimal(this, provider);
-        }
-
-        DateTime IConvertible.ToDateTime(IFormatProvider provider)
-        {
-            return Convert.ToDateTime(this, provider);
-        }
-
-        Object IConvertible.ToType(Type type, IFormatProvider provider)
-        {
-            return Convert.DefaultToType((IConvertible)this, type, provider);
-        }
-
-        // Is this a string that can be compared quickly (that is it has only characters > 0x80 
-        // and not a - or '
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal extern bool IsFastSort();
-        // Is this a string that only contains characters < 0x80.
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal extern bool IsAscii();
-
-#if FEATURE_COMINTEROP
-        // Set extra byte for odd-sized strings that came from interop as BSTR.
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal extern void SetTrailByte(byte data);
-        // Try to retrieve the extra byte - returns false if not present.
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        internal extern bool TryGetTrailByte(out byte data);
-#endif        
-
-        public CharEnumerator GetEnumerator()
-        {
-            return new CharEnumerator(this);
-        }
-
-        IEnumerator<char> IEnumerable<char>.GetEnumerator()
-        {
-            return new CharEnumerator(this);
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return new CharEnumerator(this);
-        }
-
-        // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes.
-        internal unsafe static void InternalCopy(String src, IntPtr dest, int len)
-        {
-            if (len == 0)
-                return;
-            fixed (char* charPtr = &src._firstChar)
-            {
-                byte* srcPtr = (byte*)charPtr;
-                byte* dstPtr = (byte*)dest;
-                Buffer.Memcpy(dstPtr, srcPtr, len);
-            }
-        }
-
-        internal ref char GetRawStringData()
-        {
-            return ref _firstChar;
-        }
-    }
-}
index 6f5f11b..dacec45 100644 (file)
@@ -37,6 +37,9 @@ static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 2 == METHOD__STRING__CTORF_CH
 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 3 == METHOD__STRING__CTORF_CHARPTR);
 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 4 == METHOD__STRING__CTORF_CHARPTR_START_LEN);
 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 5 == METHOD__STRING__CTORF_READONLYSPANOFCHAR);
+static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 6 == METHOD__STRING__CTORF_SBYTEPTR);
+static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 7 == METHOD__STRING__CTORF_SBYTEPTR_START_LEN);
+static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 8 == METHOD__STRING__CTORF_SBYTEPTR_START_LEN_ENCODING);
 
 // ECall::CtorCharXxx has to be in same order as METHOD__STRING__CTORF_XXX
 #define ECallCtor_First ECall::CtorCharArrayManaged
@@ -46,8 +49,11 @@ static_assert_no_msg(ECallCtor_First + 2 == ECall::CtorCharCountManaged);
 static_assert_no_msg(ECallCtor_First + 3 == ECall::CtorCharPtrManaged);
 static_assert_no_msg(ECallCtor_First + 4 == ECall::CtorCharPtrStartLengthManaged);
 static_assert_no_msg(ECallCtor_First + 5 == ECall::CtorReadOnlySpanOfCharManaged);
+static_assert_no_msg(ECallCtor_First + 6 == ECall::CtorSBytePtrManaged);
+static_assert_no_msg(ECallCtor_First + 7 == ECall::CtorSBytePtrStartLengthManaged);
+static_assert_no_msg(ECallCtor_First + 8 == ECall::CtorSBytePtrStartLengthEncodingManaged);
 
-#define NumberOfStringConstructors 6
+#define NumberOfStringConstructors 9
 
 void ECall::PopulateManagedStringConstructors()
 {
index 26fa9eb..c809109 100644 (file)
@@ -111,6 +111,9 @@ class ECall
     DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharPtrManaged,                NULL) \
     DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharPtrStartLengthManaged,     NULL) \
     DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorReadOnlySpanOfCharManaged,     NULL) \
+    DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrManaged,               NULL) \
+    DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthManaged,    NULL) \
+    DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthEncodingManaged, NULL) \
     DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread,          NULL) \
 
         enum
index 05ce0e0..5877f31 100644 (file)
@@ -103,9 +103,9 @@ FCFuncStart(gStringFuncs)
     FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrStartLengthManaged)
     FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Char_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharCountManaged)
     FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorReadOnlySpanOfCharManaged)
-    FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, COMString::StringInitCharPtr)
-    FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, COMString::StringInitCharPtrPartial)
-    FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, COMString::StringInitSBytPtrPartialEx)
+    FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrManaged)
+    FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrStartLengthManaged)
+    FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrStartLengthEncodingManaged)
     FCFuncElement("IsFastSort", COMString::IsFastSort)
     FCFuncElement("nativeCompareOrdinalIgnoreCaseWC", COMString::FCCompareOrdinalIgnoreCaseWC)
     FCIntrinsic("get_Length", COMString::Length, CORINFO_INTRINSIC_StringLength)
index 47dd024..49e26f1 100644 (file)
@@ -409,6 +409,9 @@ DEFINE_METASIG(IM(Char_Int_RetStr, u i, s))
 DEFINE_METASIG(IM(PtrChar_RetStr, P(u), s))
 DEFINE_METASIG(IM(PtrChar_Int_Int_RetStr, P(u) i i, s))
 DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetStr, GI(g(READONLY_SPAN), 1, u), s))
+DEFINE_METASIG(IM(PtrSByt_RetStr, P(B), s))
+DEFINE_METASIG(IM(PtrSByt_Int_Int_RetStr, P(B) i i, s))
+DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
 DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))
 
 DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
index 0af6c94..7642db6 100644 (file)
@@ -866,18 +866,21 @@ DEFINE_FIELD(UINTPTR,               ZERO,                   Zero)
 
 DEFINE_CLASS(BITCONVERTER,          System,                 BitConverter)
 DEFINE_FIELD(BITCONVERTER,          ISLITTLEENDIAN,         IsLittleEndian)
+
 // Defined as element type alias
 // DEFINE_CLASS(STRING,                System,                 String)
 DEFINE_FIELD(STRING,                M_FIRST_CHAR,           _firstChar)
 DEFINE_FIELD(STRING,                EMPTY,                  Empty)
-DEFINE_METHOD(STRING,               CREATE_STRING,          CreateString,               SM_PtrSByt_Int_Int_Encoding_RetStr)
 DEFINE_METHOD(STRING,               CTOR_CHARPTR,           .ctor,                      IM_PtrChar_RetVoid)
-DEFINE_METHOD(STRING,               CTORF_CHARARRAY,        CtorCharArray,              IM_ArrChar_RetStr)
-DEFINE_METHOD(STRING,               CTORF_CHARARRAY_START_LEN,CtorCharArrayStartLength, IM_ArrChar_Int_Int_RetStr)
-DEFINE_METHOD(STRING,               CTORF_CHAR_COUNT,       CtorCharCount,              IM_Char_Int_RetStr)
-DEFINE_METHOD(STRING,               CTORF_CHARPTR,          CtorCharPtr,                IM_PtrChar_RetStr)
-DEFINE_METHOD(STRING,               CTORF_CHARPTR_START_LEN,CtorCharPtrStartLength,     IM_PtrChar_Int_Int_RetStr)
-DEFINE_METHOD(STRING,               CTORF_READONLYSPANOFCHAR,CtorReadOnlySpanOfChar,    IM_ReadOnlySpanOfChar_RetStr)
+DEFINE_METHOD(STRING,               CTORF_CHARARRAY,        Ctor,                       IM_ArrChar_RetStr)
+DEFINE_METHOD(STRING,               CTORF_CHARARRAY_START_LEN,Ctor,                     IM_ArrChar_Int_Int_RetStr)
+DEFINE_METHOD(STRING,               CTORF_CHAR_COUNT,       Ctor,                       IM_Char_Int_RetStr)
+DEFINE_METHOD(STRING,               CTORF_CHARPTR,          Ctor,                       IM_PtrChar_RetStr)
+DEFINE_METHOD(STRING,               CTORF_CHARPTR_START_LEN,Ctor,                       IM_PtrChar_Int_Int_RetStr)
+DEFINE_METHOD(STRING,               CTORF_READONLYSPANOFCHAR,Ctor,                      IM_ReadOnlySpanOfChar_RetStr)
+DEFINE_METHOD(STRING,               CTORF_SBYTEPTR,         Ctor,                       IM_PtrSByt_RetStr)
+DEFINE_METHOD(STRING,               CTORF_SBYTEPTR_START_LEN, Ctor,                     IM_PtrSByt_Int_Int_RetStr)
+DEFINE_METHOD(STRING,               CTORF_SBYTEPTR_START_LEN_ENCODING, Ctor,            IM_PtrSByt_Int_Int_Encoding_RetStr)
 DEFINE_METHOD(STRING,               INTERNAL_COPY,          InternalCopy,               SM_Str_IntPtr_Int_RetVoid)
 DEFINE_METHOD(STRING,               WCSLEN,                 wcslen,                     SM_PtrChar_RetInt)
 DEFINE_PROPERTY(STRING,             LENGTH,                 Length,                     Int)
index 374fe19..0282b6a 100644 (file)
@@ -1082,62 +1082,6 @@ STRINGREF* StringObject::InitEmptyStringRefPtr() {
     return EmptyStringRefPtr;
 }
 
-/*=============================StringInitCharHelper=============================
-**Action:
-**Returns:
-**Arguments:
-**Exceptions:
-**Note this
-==============================================================================*/
-STRINGREF __stdcall StringObject::StringInitCharHelper(LPCSTR pszSource, int length) {
-    CONTRACTL {
-        THROWS;
-        GC_TRIGGERS;
-        MODE_COOPERATIVE;
-    } CONTRACTL_END;
-
-    STRINGREF pString=NULL;
-    int dwSizeRequired=0;
-     _ASSERTE(length>=-1);                        
-     
-    if (!pszSource || length == 0) {
-        return StringObject::GetEmptyString();
-    }
-#ifndef FEATURE_PAL
-    else if ((size_t)pszSource < 64000) {
-        COMPlusThrow(kArgumentException, W("Arg_MustBeStringPtrNotAtom"));
-    }    
-#endif // FEATURE_PAL
-
-    // Make sure we can read from the pointer.
-    // This is better than try to read from the pointer and catch the access violation exceptions.
-    if( length == -1) {
-        length = (INT32)strlen(pszSource);
-    }
-   
-    if(length > 0)  {  
-        dwSizeRequired=WszMultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszSource, length, NULL, 0);
-    }
-
-    if (dwSizeRequired == 0) {
-        if (length == 0) {
-            return StringObject::GetEmptyString();
-        }
-        COMPlusThrow(kArgumentException, W("Arg_InvalidANSIString"));
-    }
-
-    pString = AllocateString(dwSizeRequired);        
-    dwSizeRequired = WszMultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)pszSource, length, pString->GetBuffer(), dwSizeRequired);
-    if (dwSizeRequired == 0) {
-        COMPlusThrow(kArgumentException, W("Arg_InvalidANSIString"));
-    }
-
-    _ASSERTE(dwSizeRequired != INT32_MAX && pString->GetBuffer()[dwSizeRequired]==0);
-
-    return pString;
-}
-
-
 // strAChars must be null-terminated, with an appropriate aLength
 // strBChars must be null-terminated, with an appropriate bLength OR bLength == -1
 // If bLength == -1, we stop on the first null character in strBChars
index 5970806..b0125fa 100644 (file)
@@ -1024,7 +1024,6 @@ class StringObject : public Object
 
     static STRINGREF* InitEmptyStringRefPtr();
 
-    static STRINGREF __stdcall StringInitCharHelper(LPCSTR pszSource, int length);
     DWORD InternalCheckHighChars();
 
     BOOL HasTrailByte();