summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/classlibnative/bcltype/stringnative.cpp108
-rw-r--r--src/classlibnative/bcltype/stringnative.h10
-rw-r--r--src/mscorlib/Resources/Strings.resx8
-rw-r--r--src/mscorlib/System.Private.CoreLib.csproj2
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs20
-rw-r--r--src/mscorlib/shared/System.Private.CoreLib.Shared.projitems2
-rw-r--r--src/mscorlib/shared/System/String.cs750
-rw-r--r--src/mscorlib/src/System/Buffer.cs76
-rw-r--r--src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs22
-rw-r--r--src/mscorlib/src/System/RtType.cs2
-rw-r--r--src/mscorlib/src/System/String.CoreCLR.cs152
-rw-r--r--src/mscorlib/src/System/String.cs896
-rw-r--r--src/vm/ecall.cpp8
-rw-r--r--src/vm/ecall.h3
-rw-r--r--src/vm/ecalllist.h6
-rw-r--r--src/vm/metasig.h3
-rw-r--r--src/vm/mscorlib.h17
-rw-r--r--src/vm/object.cpp56
-rw-r--r--src/vm/object.h1
19 files changed, 960 insertions, 1182 deletions
diff --git a/src/classlibnative/bcltype/stringnative.cpp b/src/classlibnative/bcltype/stringnative.cpp
index cc91d0169b..63eed7345a 100644
--- a/src/classlibnative/bcltype/stringnative.cpp
+++ b/src/classlibnative/bcltype/stringnative.cpp
@@ -31,114 +31,6 @@
#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;
diff --git a/src/classlibnative/bcltype/stringnative.h b/src/classlibnative/bcltype/stringnative.h
index 53b7b863a6..f0df0503e5 100644
--- a/src/classlibnative/bcltype/stringnative.h
+++ b/src/classlibnative/bcltype/stringnative.h
@@ -41,16 +41,6 @@
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
//
diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx
index c8572175e8..28346b2cf5 100644
--- a/src/mscorlib/Resources/Strings.resx
+++ b/src/mscorlib/Resources/Strings.resx
@@ -532,9 +532,6 @@
<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>
@@ -3718,4 +3715,7 @@
<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>
diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj
index 46ccdf1e18..46f46ded72 100644
--- a/src/mscorlib/System.Private.CoreLib.csproj
+++ b/src/mscorlib/System.Private.CoreLib.csproj
@@ -319,7 +319,7 @@
<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
index 0000000000..158e4db3fd
--- /dev/null
+++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs
@@ -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;
+ }
+}
diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
index a0c6c0cdce..a5b49c8cff 100644
--- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
@@ -492,6 +492,7 @@
<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" />
@@ -686,6 +687,7 @@
<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
index 0000000000..e303b43bef
--- /dev/null
+++ b/src/mscorlib/shared/System/String.cs
@@ -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);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Buffer.cs b/src/mscorlib/src/System/Buffer.cs
index 185e612818..d3189599db 100644
--- a/src/mscorlib/src/System/Buffer.cs
+++ b/src/mscorlib/src/System/Buffer.cs
@@ -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)]
diff --git a/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
index 57b63eb0e1..567a22d496 100644
--- a/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
+++ b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
@@ -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.
diff --git a/src/mscorlib/src/System/RtType.cs b/src/mscorlib/src/System/RtType.cs
index cfaf13ffb3..70b9e3843f 100644
--- a/src/mscorlib/src/System/RtType.cs
+++ b/src/mscorlib/src/System/RtType.cs
@@ -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
index 0000000000..7685af21e4
--- /dev/null
+++ b/src/mscorlib/src/System/String.CoreCLR.cs
@@ -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
index 6974cdea97..0000000000
--- a/src/mscorlib/src/System/String.cs
+++ /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;
- }
- }
-}
diff --git a/src/vm/ecall.cpp b/src/vm/ecall.cpp
index 6f5f11b894..dacec45787 100644
--- a/src/vm/ecall.cpp
+++ b/src/vm/ecall.cpp
@@ -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()
{
diff --git a/src/vm/ecall.h b/src/vm/ecall.h
index 26fa9eb478..c809109c4c 100644
--- a/src/vm/ecall.h
+++ b/src/vm/ecall.h
@@ -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
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index 05ce0e0ec2..5877f3183d 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -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)
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 47dd024bde..49e26f17e9 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -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))
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index 0af6c94bc9..7642db6638 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -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)
diff --git a/src/vm/object.cpp b/src/vm/object.cpp
index 374fe192f2..0282b6a6a4 100644
--- a/src/vm/object.cpp
+++ b/src/vm/object.cpp
@@ -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
diff --git a/src/vm/object.h b/src/vm/object.h
index 5970806e71..b0125fa50e 100644
--- a/src/vm/object.h
+++ b/src/vm/object.h
@@ -1024,7 +1024,6 @@ class StringObject : public Object
static STRINGREF* InitEmptyStringRefPtr();
- static STRINGREF __stdcall StringInitCharHelper(LPCSTR pszSource, int length);
DWORD InternalCheckHighChars();
BOOL HasTrailByte();