// 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.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.Contracts; 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. [ComVisible(true)] [Serializable] public sealed partial class String : IComparable, ICloneable, IConvertible, IEnumerable , IComparable, IEnumerable, IEquatable { // //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 m_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 m_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. // [System.Security.SecuritySafeCritical] // auto-generated internal unsafe static string SmallCharToUpper(string strIn) { Contract.Requires(strIn != null); Contract.EndContractBlock(); // // 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.m_firstChar, outBuff = &strOut.m_firstChar) { for(int i = 0; i < length; i++) { int c = inBuff[i]; Contract.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; } Contract.Assert(outBuff[length]=='\0', "outBuff[length]=='\0'"); } return strOut; } // Gets the character at a specified position. // // Spec#: Apply the precondition here using a contract assembly. Potential perf issue. [System.Runtime.CompilerServices.IndexerName("Chars")] public extern char this[int index] { [MethodImpl(MethodImplOptions.InternalCall)] [System.Security.SecuritySafeCritical] // public member 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. // [System.Security.SecuritySafeCritical] // auto-generated unsafe public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { if (destination == null) throw new ArgumentNullException("destination"); if (count < 0) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount")); if (sourceIndex < 0) throw new ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); if (count > Length - sourceIndex) throw new ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); if (destinationIndex > destination.Length - count || destinationIndex < 0) throw new ArgumentOutOfRangeException("destinationIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); Contract.EndContractBlock(); // Note: fixed does not like empty arrays if (count > 0) { fixed (char* src = &this.m_firstChar) fixed (char* dest = destination) wstrcpy(dest + destinationIndex, src + sourceIndex, count); } } // Returns the entire string as an array of characters. [System.Security.SecuritySafeCritical] // auto-generated unsafe public char[] ToCharArray() { int length = Length; if (length > 0) { char[] chars = new char[length]; fixed (char* src = &this.m_firstChar) fixed (char* dest = chars) { wstrcpy(dest, src, length); } return chars; } #if FEATURE_CORECLR return Array.Empty(); #else return new char[0]; #endif } // Returns a substring of this string as an array of characters. // [System.Security.SecuritySafeCritical] // auto-generated unsafe public char[] ToCharArray(int startIndex, int length) { // Range check everything. if (startIndex < 0 || startIndex > Length || startIndex > Length - length) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); if (length < 0) throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Index")); Contract.EndContractBlock(); if (length > 0) { char[] chars = new char[length]; fixed (char* src = &this.m_firstChar) fixed (char* dest = chars) { wstrcpy(dest, src + startIndex, length); } return chars; } #if FEATURE_CORECLR return Array.Empty(); #else return new char[0]; #endif } [Pure] public static bool IsNullOrEmpty(String value) { return (value == null || value.Length == 0); } [Pure] 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. // // Spec#: Add postcondition in a contract assembly. Potential perf problem. public extern int Length { [System.Security.SecuritySafeCritical] // auto-generated [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. // [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe public extern String(char *value); [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe public extern String(char *value, int startIndex, int length); [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe public extern String(sbyte *value); [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe public extern String(sbyte *value, int startIndex, int length); [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe public extern String(sbyte *value, int startIndex, int length, Encoding enc); [System.Security.SecurityCritical] // auto-generated 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("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex")); if ((value + startIndex) < value) { // overflow check throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("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("value", Environment.GetResourceString("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 [System.Security.SecurityCritical] // auto-generated unsafe static internal String CreateStringFromEncoding( byte* bytes, int byteLength, Encoding encoding) { Contract.Requires(bytes != null); Contract.Requires(byteLength >= 0); // Get our string length int stringLength = encoding.GetCharCount(bytes, byteLength, null); Contract.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.m_firstChar) { int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null); Contract.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 m_firstChar is a private member. // Making m_firstChar internal would be dangerous since it would make it much easier to break String's immutability. [SecuritySafeCritical] internal static string CreateFromChar(char c) { string result = FastAllocateString(1); result.m_firstChar = c; return result; } [System.Security.SecuritySafeCritical] // auto-generated unsafe internal int GetBytesFromEncoding(byte* pbNativeBuffer, int cbNativeBuffer,Encoding encoding) { // encoding == Encoding.UTF8 fixed (char* pwzChar = &this.m_firstChar) { return encoding.GetBytes(pwzChar, m_stringLength, pbNativeBuffer, cbNativeBuffer); } } [System.Security.SecuritySafeCritical] // auto-generated unsafe internal int ConvertToAnsi(byte *pbNativeBuffer, int cbNativeBuffer, bool fBestFit, bool fThrowOnUnmappableChar) { Contract.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 = &this.m_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(Environment.GetResourceString("Interop_Marshal_Unmappable_Char")); } pbNativeBuffer[nb] = 0; return nb; } // Normalization Methods // These just wrap calls to Normalization class public bool IsNormalized() { #if !FEATURE_NORM_IDNA_ONLY // Default to Form C return IsNormalized(NormalizationForm.FormC); #else // Default to Form IDNA return IsNormalized((NormalizationForm)ExtendedNormalizationForms.FormIdna); #endif } [System.Security.SecuritySafeCritical] // auto-generated public bool IsNormalized(NormalizationForm normalizationForm) { #if !FEATURE_NORM_IDNA_ONLY 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 // !FEATURE_NORM_IDNA_ONLY return Normalization.IsNormalized(this, normalizationForm); } public String Normalize() { #if !FEATURE_NORM_IDNA_ONLY // Default to Form C return Normalize(NormalizationForm.FormC); #else // Default to Form IDNA return Normalize((NormalizationForm)ExtendedNormalizationForms.FormIdna); #endif } [System.Security.SecuritySafeCritical] // auto-generated public String Normalize(NormalizationForm normalizationForm) { #if !FEATURE_NORM_IDNA_ONLY 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 // !FEATURE_NORM_IDNA_ONLY return Normalization.Normalize(this, normalizationForm); } [System.Security.SecurityCritical] // auto-generated [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. // [System.Security.SecuritySafeCritical] // auto-generated [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. // [System.Security.SecuritySafeCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] public extern String(char [] value); [System.Security.SecurityCritical] // auto-generated 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) } [System.Security.SecuritySafeCritical] // auto-generated private String CtorCharArray(char [] value) { if (value != null && value.Length != 0) { String result = FastAllocateString(value.Length); unsafe { fixed (char* dest = &result.m_firstChar, source = value) { wstrcpy(dest, source, value.Length); } } return result; } else return String.Empty; } [System.Security.SecuritySafeCritical] // auto-generated private String CtorCharArrayStartLength(char [] value, int startIndex, int length) { if (value == null) throw new ArgumentNullException("value"); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex")); if (length < 0) throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength")); if (startIndex > value.Length - length) throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); Contract.EndContractBlock(); if (length > 0) { String result = FastAllocateString(length); unsafe { fixed (char* dest = &result.m_firstChar, source = value) { wstrcpy(dest, source + startIndex, length); } } return result; } else return String.Empty; } [System.Security.SecuritySafeCritical] // auto-generated private String CtorCharCount(char c, int count) { if (count > 0) { String result = FastAllocateString(count); if (c != 0) { unsafe { fixed (char* dest = &result.m_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("count", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", "count")); } [System.Security.SecurityCritical] // auto-generated private 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; } Contract.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: Contract.Assert(*end == 0); int count = (int)(end - ptr); return count; } [System.Security.SecurityCritical] // auto-generated private unsafe String CtorCharPtr(char *ptr) { if (ptr == null) return String.Empty; #if !FEATURE_PAL if (ptr < (char*)64000) throw new ArgumentException(Environment.GetResourceString("Arg_MustBeStringPtrNotAtom")); #endif // FEATURE_PAL Contract.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.m_firstChar) wstrcpy(dest, ptr, count); return result; } catch (NullReferenceException) { throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR")); } } [System.Security.SecurityCritical] // auto-generated private unsafe String CtorCharPtrStartLength(char *ptr, int startIndex, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength")); } if (startIndex < 0) { throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex")); } Contract.EndContractBlock(); Contract.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("startIndex", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR")); } if (length == 0) return String.Empty; String result = FastAllocateString(length); try { fixed (char* dest = &result.m_firstChar) wstrcpy(dest, pFrom, length); return result; } catch (NullReferenceException) { throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR")); } } [System.Security.SecuritySafeCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] public extern String(char c, int count); // Returns this string. public override String ToString() { Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return this; } public String ToString(IFormatProvider provider) { Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); 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() { Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return this; } [System.Security.SecuritySafeCritical] // auto-generated unsafe public static String Copy (String str) { if (str==null) { throw new ArgumentNullException("str"); } Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); int length = str.Length; String result = FastAllocateString(length); fixed(char* dest = &result.m_firstChar) fixed(char* src = &str.m_firstChar) { wstrcpy(dest, src, length); } return result; } [System.Security.SecuritySafeCritical] // auto-generated public static String Intern(String str) { if (str==null) { throw new ArgumentNullException("str"); } Contract.Ensures(Contract.Result().Length == str.Length); Contract.Ensures(str.Equals(Contract.Result())); Contract.EndContractBlock(); return Thread.GetDomain().GetOrInternString(str); } [Pure] [System.Security.SecuritySafeCritical] // auto-generated public static String IsInterned(String str) { if (str==null) { throw new ArgumentNullException("str"); } Contract.Ensures(Contract.Result() == null || Contract.Result().Length == str.Length); Contract.EndContractBlock(); 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 ' [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern bool IsFastSort(); // Is this a string that only contains characters < 0x80. [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern bool IsAscii(); // Set extra byte for odd-sized strings that came from interop as BSTR. [System.Security.SecurityCritical] [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern void SetTrailByte(byte data); // Try to retrieve the extra byte - returns false if not present. [System.Security.SecurityCritical] [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern bool TryGetTrailByte(out byte data); #if !FEATURE_CORECLR public CharEnumerator GetEnumerator() { Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead."); return new CharEnumerator(this); } #endif // !FEATURE_CORECLR IEnumerator IEnumerable.GetEnumerator() { Contract.Ensures(Contract.Result>() != null); Contract.EndContractBlock(); BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead."); return new CharEnumerator(this); } /// IEnumerator IEnumerable.GetEnumerator() { Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead."); return new CharEnumerator(this); } // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes. [System.Security.SecurityCritical] // auto-generated internal unsafe static void InternalCopy(String src, IntPtr dest,int len) { if (len == 0) return; fixed(char* charPtr = &src.m_firstChar) { byte* srcPtr = (byte*) charPtr; byte* dstPtr = (byte*) dest; Buffer.Memcpy(dstPtr, srcPtr, len); } } } }