diff options
-rw-r--r-- | src/classlibnative/bcltype/stringnative.cpp | 108 | ||||
-rw-r--r-- | src/classlibnative/bcltype/stringnative.h | 10 | ||||
-rw-r--r-- | src/mscorlib/Resources/Strings.resx | 8 | ||||
-rw-r--r-- | src/mscorlib/System.Private.CoreLib.csproj | 2 | ||||
-rw-r--r-- | src/mscorlib/shared/Interop/Windows/Kernel32/Interop.MultiByteToWideChar.cs | 20 | ||||
-rw-r--r-- | src/mscorlib/shared/System.Private.CoreLib.Shared.projitems | 2 | ||||
-rw-r--r-- | src/mscorlib/shared/System/String.cs | 750 | ||||
-rw-r--r-- | src/mscorlib/src/System/Buffer.cs | 76 | ||||
-rw-r--r-- | src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs | 22 | ||||
-rw-r--r-- | src/mscorlib/src/System/RtType.cs | 2 | ||||
-rw-r--r-- | src/mscorlib/src/System/String.CoreCLR.cs | 152 | ||||
-rw-r--r-- | src/mscorlib/src/System/String.cs | 896 | ||||
-rw-r--r-- | src/vm/ecall.cpp | 8 | ||||
-rw-r--r-- | src/vm/ecall.h | 3 | ||||
-rw-r--r-- | src/vm/ecalllist.h | 6 | ||||
-rw-r--r-- | src/vm/metasig.h | 3 | ||||
-rw-r--r-- | src/vm/mscorlib.h | 17 | ||||
-rw-r--r-- | src/vm/object.cpp | 56 | ||||
-rw-r--r-- | src/vm/object.h | 1 |
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(); |