// ==++== // // // Copyright (c) 2002 Microsoft Corporation. All rights reserved. // // The use and distribution terms for this software are contained in the file // named license.txt, which can be found in the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by the // terms of this license. // // You must not remove this notice, or any other, from this software. // // // ==--== /**//*============================================================ ** ** Class: String ** ** ** ** Purpose: Contains headers for the String class. Actual implementations ** are in String.cpp ** ** Date: March 15, 1998 ** ===========================================================*/ namespace System { using System.Text; using System; using System.Globalization; using System.Threading; using System.Collections; using System.Runtime.CompilerServices; using va_list = System.ArgIterator; using __UnmanagedMemoryStream = System.IO.__UnmanagedMemoryStream; // // 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. // // When passing a null string into a constructor in VJ and VC, the null should be // explicitly type cast to a String. // For Example: // String s = new String((String)null); // Text.Out.WriteLine(s); // /**////<include file='doc\String.uex' path='docs/doc[@for="String"]/*' /> [Serializable] publicsealedclass String : IComparable, ICloneable, IConvertible, IEnumerable { // //NOTE NOTE NOTE NOTE //These fields map directly onto the fields in an EE StringObject. See object.h for the layout. // [NonSerialized]privateint m_arrayLength; [NonSerialized]privateint m_stringLength; [NonSerialized]privatechar m_firstChar; //private static readonly char FmtMsgMarkerChar='%'; //private static readonly char FmtMsgFmtCodeChar='!'; //These are defined in Com99/src/vm/COMStringCommon.h and must be kept in sync. privateconstint TrimHead =0; privateconstint TrimTail =1; privateconstint TrimBoth =2; // The Empty constant holds the empty string value. //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. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Empty"]/*' /> publicstaticreadonly String Empty =""; // //Native Static Methods // // Joins an array of strings together as one string with a separator between each original string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Join"]/*' /> publicstatic String Join (String separator, String[] value) { if (value==null) { thrownew ArgumentNullException("value"); } return Join(separator, value, 0, value.Length); } // Joins an array of strings together as one string with a separator between each original string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Join1"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicstaticextern String Join (String separator, String[] value, int startIndex, int count); [MethodImplAttribute(MethodImplOptions.InternalCall)] internalstaticexternint nativeCompareOrdinal(String strA, String strB, bool bIgnoreCase); [MethodImplAttribute(MethodImplOptions.InternalCall)] internalstaticexternint nativeCompareOrdinalEx(String strA, int indexA, String strB, int indexB, int count); //This will not work in case-insensitive mode for any character greater than 0x80. //We'll throw an ArgumentException. [MethodImplAttribute(MethodImplOptions.InternalCall)] unsafeinternalstaticexternint nativeCompareOrdinalWC(String strA, char*strBChars, bool bIgnoreCase, outbool success); // // 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. // internalstatic String SmallCharToUpper(String strA) { String newString = FastAllocateString(strA.Length); nativeSmallCharToUpper(strA, newString); return newString; } [MethodImplAttribute(MethodImplOptions.InternalCall)] privatestaticexternvoid nativeSmallCharToUpper(String strIn, String strOut); // This is a helper method for the security team. They need to construct strings from a char[] // within their homebrew XML parser. They guarantee that the char[] they pass in isn't null and // that the provided indices are valid so we just stuff real fast. internalstatic String CreateFromCharArray( char[] array, int start, int count ) { String newString = FastAllocateString( count ); FillStringArray( newString, 0, array, start, count ); return newString; } // // // NATIVE INSTANCE METHODS // // // // Search/Query methods // // Determines whether two strings match. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Equals"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternoverridebool Equals(Object obj); // Determines whether two strings match. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Equals1"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternbool Equals(String value); // Determines whether two Strings match. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Equals2"]/*' /> publicstaticbool Equals(String a, String b) { if ((Object)a==(Object)b) { returntrue; } if ((Object)a==null|| (Object)b==null) { returnfalse; } return a.Equals(b); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.operatorEQ"]/*' /> publicstaticbooloperator== (String a, String b) { return String.Equals(a, b); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.operatorNE"]/*' /> publicstaticbooloperator!= (String a, String b) { return!String.Equals(a, b); } [MethodImplAttribute(MethodImplOptions.InternalCall)] internalexternchar InternalGetChar(int index); // Gets the character at a specified position. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.this"]/*' /> [System.Runtime.CompilerServices.IndexerName("Chars")] publiccharthis[int index] { get{ return InternalGetChar(index); } } // Converts a substring of this string to an array of characters. Copies the // characters of this string beginning at position startIndex and ending at // startIndex + length - 1 to the character array buffer, beginning // at bufferStartIndex. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.CopyTo"]/*' /> publicvoid CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { if (destination ==null) thrownew ArgumentNullException("destination"); if (count <0) thrownew ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount")); if (sourceIndex <0) thrownew ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); if (count > Length - sourceIndex) thrownew ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); if (destinationIndex > destination.Length-count || destinationIndex <0) thrownew ArgumentOutOfRangeException("destinationIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); InternalCopyTo(sourceIndex, destination, destinationIndex, count); } [MethodImplAttribute(MethodImplOptions.InternalCall)] internalexternvoid InternalCopyTo(int sourceIndex, char[] destination, int destinationIndex, int count); [MethodImplAttribute(MethodImplOptions.InternalCall)] internalexternvoid CopyToByteArray(int sourceIndex, byte[] destination, int destinationIndex, int charCount); // Returns the entire string as an array of characters. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToCharArray"]/*' /> publicchar[] ToCharArray() { return ToCharArray(0,Length); } // Returns a substring of this string as an array of characters. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToCharArray1"]/*' /> publicchar[] ToCharArray(int startIndex, int length) { // Range check everything. if (startIndex <0|| startIndex > Length || startIndex > Length - length) thrownew ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); if (length <0) thrownew ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Index")); char[] chars =newchar[length]; InternalCopyTo(startIndex, chars, 0, length); return chars; } // Gets a hash code for this string. If strings A and B are such that A.Equals(B), then // they will return the same hash code. /**////<include file='doc\String.uex' path='docs/doc[@for="String.GetHashCode"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternoverrideint GetHashCode(); // Gets the length of this string /**////<include file='doc\String.uex' path='docs/doc[@for="String.Length"]/*' /> publicint Length { get{ return InternalLength(); } } /**//// This is a EE implemented function so that the JIT can recognise is specially /// and eliminate checks on character fetchs. [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternint InternalLength(); /**////<internalonly/> internalint ArrayLength { get{ return (m_arrayLength); } } // Used by StringBuilder internalint Capacity { get{ return (m_arrayLength -1); } } // Creates an array of strings by splitting this string at each // occurence of a separator. The separator is searched for, and if found, // the substring preceding the occurence is stored as the first element in // the array of strings. We then continue in this manner by searching // the substring that follows the occurence. On the other hand, if the separator // is not found, the array of strings will contain this instance as its only element. // If the separator is null // whitespace (i.e., Character.IsWhitespace) is used as the separator. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Split"]/*' /> public String [] Split(paramschar [] separator) { return Split(separator, Int32.MaxValue); } // Creates an array of strings by splitting this string at each // occurence of a separator. The separator is searched for, and if found, // the substring preceding the occurence is stored as the first element in // the array of strings. We then continue in this manner by searching // the substring that follows the occurence. On the other hand, if the separator // is not found, the array of strings will contain this instance as its only element. // If the spearator is the empty string (i.e., String.Empty), then // whitespace (i.e., Character.IsWhitespace) is used as the separator. // If there are more than count different strings, the last n-(count-1) // elements are concatenated and added as the last String. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Split1"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String[] Split(char[] separator, int count); // Returns a substring of this string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Substring"]/*' /> public String Substring (int startIndex) { returnthis.Substring (startIndex, Length-startIndex); } // Returns a substring of this string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Substring1"]/*' /> public String Substring (int startIndex, int length) { int thisLength = Length; //Bounds Checking. if (startIndex<0) { thrownew ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex")); } if (length<0) { thrownew ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength")); } if (startIndex > thisLength-length) { thrownew ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_IndexLength")); } String s = FastAllocateString(length); FillSubstring(s, 0, this, startIndex, length); return s; } [MethodImplAttribute(MethodImplOptions.InternalCall)] internalextern String TrimHelper(char[] trimChars, int trimType); //This should really live on System.Globalization.CharacterInfo. However, //Trim gets called by security while resgen is running, so we can't run //CharacterInfo's class initializer (which goes to native and looks for a //resource table that hasn't yet been attached to the assembly when resgen //runs. internalstaticreadonlychar[] WhitespaceChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD, (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004, (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B, (char) 0x3000, (char) 0xFEFF }; // Removes a string of characters from the ends of this string. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Trim"]/*' /> public String Trim(paramschar[] trimChars) { if (null==trimChars || trimChars.Length ==0) { trimChars=WhitespaceChars; } return TrimHelper(trimChars,TrimBoth); } // Removes a string of characters from the beginning of this string. /**////<include file='doc\String.uex' path='docs/doc[@for="String.TrimStart"]/*' /> public String TrimStart(paramschar[] trimChars) { if (null==trimChars || trimChars.Length ==0) { trimChars=WhitespaceChars; } return TrimHelper(trimChars,TrimHead); } // Removes a string of characters from the end of this string. /**////<include file='doc\String.uex' path='docs/doc[@for="String.TrimEnd"]/*' /> public String TrimEnd(paramschar[] trimChars) { if (null==trimChars || trimChars.Length ==0) { trimChars=WhitespaceChars; } return TrimHelper(trimChars,TrimTail); } // Creates a new string with the characters copied in from ptr. If // ptr is null, a string initialized to ";<;No Object>;"; (i.e., // String.NullString) is created. // // Issue: This method is only accessible from VC. /**////<include file='doc\String.uex' path='docs/doc[@for="String.String"]/*' /> [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafepublicextern String(char*value); /**////<include file='doc\String.uex' path='docs/doc[@for="String.String1"]/*' /> [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafepublicextern String(char*value, int startIndex, int length); /**////<include file='doc\String.uex' path='docs/doc[@for="String.String2"]/*' /> [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafepublicextern String(sbyte*value); /**////<include file='doc\String.uex' path='docs/doc[@for="String.String3"]/*' /> [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafepublicextern String(sbyte*value, int startIndex, int length); /**////<include file='doc\String.uex' path='docs/doc[@for="String.String4"]/*' /> [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)] unsafepublicextern String(sbyte*value, int startIndex, int length, Encoding enc); unsafestaticprivate String CreateString(sbyte*value, int startIndex, int length, Encoding enc) { if (enc ==null) returnnew String(value, startIndex, length); // default to ANSI if (length <0) thrownew ArgumentOutOfRangeException("length",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); byte [] b =newbyte[length]; __UnmanagedMemoryStream.memcpy((byte*)value, startIndex, b, 0, length); return enc.GetString(b); } // For ASCIIEncoding::GetString() unsafestaticinternal String CreateStringFromASCII(byte[] bytes, int startIndex, int length) { BCLDebug.Assert(bytes !=null, "need a byte[]."); BCLDebug.Assert(startIndex >=0&& (startIndex < bytes.Length || bytes.Length ==0), "startIndex >= 0 && startIndex < bytes.Length"); BCLDebug.Assert(length >=0&& length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex"); if (length ==0) return String.Empty; String s = FastAllocateString(length); fixed(char* pChars =&s.m_firstChar) { for(int i=0; i<length; i++) pChars[i] = (char) (bytes[i+startIndex] &0x7f); } return s; } [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstatic String FastAllocateString(int length); [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstaticvoid FillString(String dest, int destPos, String src); [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstaticvoid FillStringChecked(String dest, int destPos, String src); [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstaticvoid FillStringEx(String dest, int destPos, String src,int srcLength); [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstaticvoid FillStringArray(String dest, int stringStart, char[] array, int charStart, int count); [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternstaticvoid FillSubstring(String dest, int destPos, String src, int startPos, int count); // 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. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.String7"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern 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. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.String5"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String(char [] value); /**////<include file='doc\String.uex' path='docs/doc[@for="String.String6"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String(char c, int count); // // // INSTANCE METHODS // // // Provides a culture-correct string comparison. StrA is compared to StrB // to determine whether it is lexicographically less, equal, or greater, and then returns // either a negative integer, 0, or a positive integer; respectively. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare"]/*' /> publicstaticint Compare(String strA, String strB) { return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None); } // Provides a culture-correct string comparison. strA is compared to strB // to determine whether it is lexicographically less, equal, or greater, and then a // negative integer, 0, or a positive integer is returned; respectively. // The case-sensitive option is set by ignoreCase // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare1"]/*' /> publicstaticint Compare(String strA, String strB, bool ignoreCase) { if (ignoreCase) { return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); } return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None); } // Provides a culture-correct string comparison. strA is compared to strB // to determine whether it is lexicographically less, equal, or greater, and then a // negative integer, 0, or a positive integer is returned; respectively. // The case-sensitive option is set by ignoreCase, and the culture is set // by culture // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare2"]/*' /> publicstaticint Compare(String strA, String strB, bool ignoreCase, CultureInfo culture) { if (culture==null) { thrownew ArgumentNullException("culture"); } if (ignoreCase) { return culture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); } return culture.CompareInfo.Compare(strA, strB, CompareOptions.None); } // Determines whether two string regions match. The substring of strA beginning // at indexA of length count is compared with the substring of strB // beginning at indexB of the same length. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare3"]/*' /> publicstaticint Compare(String strA, int indexA, String strB, int indexB, int length) { int lengthA = length; int lengthB = length; if (strA!=null) { if (strA.Length - indexA < lengthA) { lengthA = (strA.Length - indexA); } } if (strB!=null) { if (strB.Length - indexB < lengthB) { lengthB = (strB.Length - indexB); } } return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); } // Determines whether two string regions match. The substring of strA beginning // at indexA of length count is compared with the substring of strB // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare4"]/*' /> publicstaticint Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase) { int lengthA = length; int lengthB = length; if (strA!=null) { if (strA.Length - indexA < lengthA) { lengthA = (strA.Length - indexA); } } if (strB!=null) { if (strB.Length - indexB < lengthB) { lengthB = (strB.Length - indexB); } } if (ignoreCase) { return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); } return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); } // Determines whether two string regions match. The substring of strA beginning // at indexA of length length is compared with the substring of strB // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean, // and the culture is set by culture. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Compare5"]/*' /> publicstaticint Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture) { if (culture==null) { thrownew ArgumentNullException("culture"); } int lengthA = length; int lengthB = length; if (strA!=null) { if (strA.Length - indexA < lengthA) { lengthA = (strA.Length - indexA); } } if (strB!=null) { if (strB.Length - indexB < lengthB) { lengthB = (strB.Length - indexB); } } if (ignoreCase) { return culture.CompareInfo.Compare(strA,indexA,lengthA, strB, indexB, lengthB,CompareOptions.IgnoreCase); }else{ return culture.CompareInfo.Compare(strA,indexA,lengthA, strB, indexB, lengthB,CompareOptions.None); } } // Compares this object to another object, returning an integer that // indicates the relationship. This method returns a value less than 0 if this is less than value, 0 // if this is equal to value, or a value greater than 0 // if this is greater than value. Strings are considered to be // greater than all non-String objects. Note that this means sorted // arrays would contain nulls, other objects, then Strings in that order. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.CompareTo"]/*' /> publicint CompareTo(Object value) { if (value ==null) { return1; } if (!(value is String)) { thrownew ArgumentException(Environment.GetResourceString("Arg_MustBeString")); } return String.Compare(this,(String)value); } // Determines the sorting relation of StrB to the current instance. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.CompareTo1"]/*' /> publicint CompareTo(String strB) { if (strB==null) { return1; } return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, 0); } // Compares strA and strB using an ordinal (code-point) comparison. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.CompareOrdinal"]/*' /> publicstaticint CompareOrdinal(String strA, String strB) { if (strA ==null|| strB ==null) { if ((Object)strA==(Object)strB) { //they're both null; return0; } return (strA==null)?-1 : 1; //-1 if A is null, 1 if B is null. } return nativeCompareOrdinal(strA, strB, false); } // Compares strA and strB using an ordinal (code-point) comparison. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.CompareOrdinal1"]/*' /> publicstaticint CompareOrdinal(String strA, int indexA, String strB, int indexB, int length) { if (strA ==null|| strB ==null) { if ((Object)strA==(Object)strB) { //they're both null; return0; } return (strA==null)?-1 : 1; //-1 if A is null, 1 if B is null. } return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length); } // Determines whether a specified string is a suffix of the the current instance. // // The case-sensitive and culture-sensitive option is set by options, // and the default culture is used. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.EndsWith"]/*' /> publicbool EndsWith(String value) { if (null==value) { thrownew ArgumentNullException("value"); } int valueLen = value.Length; int thisLen =this.Length; if (valueLen>thisLen) { returnfalse; } return (0==Compare(this, thisLen-valueLen, value, 0, valueLen)); } internalbool EndsWith(char value) { int thisLen =this.Length; if (thisLen !=0) { if (this[thisLen -1] == value) returntrue; } returnfalse; } // Returns the index of the first occurance of value in the current instance. // The search starts at startIndex and runs thorough the next count characters. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf"]/*' /> publicint IndexOf(char value) { return IndexOf(value, 0, this.Length); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf1"]/*' /> publicint IndexOf(char value, int startIndex) { return IndexOf(value, startIndex, this.Length - startIndex); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf2"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternint IndexOf(char value, int startIndex, int count); // Returns the index of the first occurance of any character in value in the current instance. // The search starts at startIndex and runs to endIndex-1. [startIndex,endIndex). // /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOfAny1"]/*' /> publicint IndexOfAny(char [] anyOf) { return IndexOfAny(anyOf,0, this.Length); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOfAny2"]/*' /> publicint IndexOfAny(char [] anyOf, int startIndex) { return IndexOfAny(anyOf, startIndex, this.Length - startIndex); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOfAny3"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternint IndexOfAny(char [] anyOf, int startIndex, int count); // Determines the position within this string of the first occurence of the specified // string, according to the specified search criteria. The search begins at // the first character of this string, it is case-sensitive and culture-sensitive, // and the default culture is used. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf6"]/*' /> publicint IndexOf(String value) { return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this,value); } // Determines the position within this string of the first occurence of the specified // string, according to the specified search criteria. The search begins at // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf7"]/*' /> publicint IndexOf(String value, int startIndex){ return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this,value,startIndex); } // Determines the position within this string of the first occurence of the specified // string, according to the specified search criteria. The search begins at // startIndex, ends at endIndex and the default culture is used. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.IndexOf8"]/*' /> publicint IndexOf(String value, int startIndex, int count){ if (startIndex + count >this.Length) { thrownew ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index")); } return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); } // Returns the index of the last occurance of value in the current instance. // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. // The character at position startIndex is included in the search. startIndex is the larger // index within the string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf"]/*' /> publicint LastIndexOf(char value) { return LastIndexOf(value, this.Length-1, this.Length); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf1"]/*' /> publicint LastIndexOf(char value, int startIndex){ return LastIndexOf(value,startIndex,startIndex +1); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf2"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternint LastIndexOf(char value, int startIndex, int count); // Returns the index of the last occurance of any character in value in the current instance. // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. // The character at position startIndex is included in the search. startIndex is the larger // index within the string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOfAny1"]/*' /> publicint LastIndexOfAny(char [] anyOf) { return LastIndexOfAny(anyOf,this.Length-1,this.Length); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOfAny2"]/*' /> publicint LastIndexOfAny(char [] anyOf, int startIndex) { return LastIndexOfAny(anyOf,startIndex,startIndex +1); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOfAny3"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicexternint LastIndexOfAny(char [] anyOf, int startIndex, int count); // Returns the index of the last occurance of any character in value in the current instance. // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. // The character at position startIndex is included in the search. startIndex is the larger // index within the string. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf6"]/*' /> publicint LastIndexOf(String value) { return LastIndexOf(value, this.Length-1,this.Length); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf7"]/*' /> publicint LastIndexOf(String value, int startIndex) { return LastIndexOf(value, startIndex, startIndex +1); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.LastIndexOf8"]/*' /> publicint LastIndexOf(String value, int startIndex, int count) { if (count<0) { thrownew ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count")); } return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); } // // /**////<include file='doc\String.uex' path='docs/doc[@for="String.PadLeft"]/*' /> public String PadLeft(int totalWidth) { return PadHelper(totalWidth, '', false); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.PadLeft1"]/*' /> public String PadLeft(int totalWidth, char paddingChar) { return PadHelper(totalWidth, paddingChar, false); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.PadRight"]/*' /> public String PadRight(int totalWidth) { return PadHelper(totalWidth, '', true); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.PadRight1"]/*' /> public String PadRight(int totalWidth, char paddingChar) { return PadHelper(totalWidth, paddingChar, true); } [MethodImplAttribute(MethodImplOptions.InternalCall)] privateextern String PadHelper(int totalWidth, char paddingChar, bool isRightPadded); // Determines whether a specified string is a prefix of the current instance // /**////<include file='doc\String.uex' path='docs/doc[@for="String.StartsWith"]/*' /> publicbool StartsWith(String value) { if (null==value) { thrownew ArgumentNullException("value"); } if (this.Length<value.Length) { returnfalse; } return (0==Compare(this,0, value,0, value.Length)); } // Creates a copy of this string in lower case. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToLower"]/*' /> public String ToLower() { returnthis.ToLower(CultureInfo.CurrentCulture); } // Creates a copy of this string in lower case. The culture is set by culture. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToLower1"]/*' /> public String ToLower(CultureInfo culture) { if (culture==null) { thrownew ArgumentNullException("culture"); } return culture.TextInfo.ToLower(this); } // Creates a copy of this string in upper case. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToUpper"]/*' /> public String ToUpper() { returnthis.ToUpper(CultureInfo.CurrentCulture); } // Creates a copy of this string in upper case. The culture is set by culture. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToUpper1"]/*' /> public String ToUpper(CultureInfo culture) { if (culture==null) { thrownew ArgumentNullException("culture"); } return culture.TextInfo.ToUpper(this); } // Returns this string. /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToString"]/*' /> publicoverride String ToString() { returnthis; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.ToString1"]/*' /> public String ToString(IFormatProvider provider) { returnthis; } // Method required for the ICloneable interface. // There's no point in cloning a string since they're immutable, so we simply return this. /**////<include file='doc\String.uex' path='docs/doc[@for="String.Clone"]/*' /> public Object Clone() { returnthis; } // Trims the whitespace from both ends of the string. Whitespace is defined by // CharacterInfo.WhitespaceChars. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Trim1"]/*' /> public String Trim() { returnthis.Trim(WhitespaceChars); } // // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Insert"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String Insert(int startIndex, String value); // Replaces all instances of oldChar with newChar. // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Replace"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String Replace (char oldChar, char newChar); // This method contains the same functionality as StringBuilder Replace. The only difference is that // a new String has to be allocated since Strings are immutable /**////<include file='doc\String.uex' path='docs/doc[@for="String.Replace1"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String Replace (String oldValue, String newValue); // // /**////<include file='doc\String.uex' path='docs/doc[@for="String.Remove"]/*' /> [MethodImplAttribute(MethodImplOptions.InternalCall)] publicextern String Remove(int startIndex, int count); /**////<include file='doc\String.uex' path='docs/doc[@for="String.Format"]/*' /> publicstatic String Format(String format, Object arg0) { return Format(null, format, new Object[] {arg0}); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Format1"]/*' /> publicstatic String Format(String format, Object arg0, Object arg1) { return Format(null, format, new Object[] {arg0, arg1}); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Format2"]/*' /> publicstatic String Format(String format, Object arg0, Object arg1, Object arg2) { return Format(null, format, new Object[] {arg0, arg1, arg2}); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Format3"]/*' /> publicstatic String Format(String format, params Object[] args) { return Format(null, format, args); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Format4"]/*' /> publicstatic String Format( IFormatProvider provider, String format, params Object[] args) { if (format ==null|| args ==null) thrownew ArgumentNullException((format==null)?"format":"args"); StringBuilder sb =new StringBuilder(format.Length + args.Length *8); sb.AppendFormat(provider,format,args); return sb.ToString(); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Copy"]/*' /> publicstatic String Copy (String str) { if (str==null) { thrownew ArgumentNullException("str"); } int length = str.Length; String result = FastAllocateString(length); FillString(result, 0, str); return result; } // Used by StringBuilder to avoid data corruption internalstatic String InternalCopy (String str) { int length = str.Length; String result = FastAllocateString(length); FillStringEx(result, 0, str, length); // The underlying's String can changed length is StringBuilder return result; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat"]/*' /> publicstatic String Concat(Object arg0) { if (arg0==null) { return String.Empty; } return arg0.ToString(); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat1"]/*' /> publicstatic String Concat(Object arg0, Object arg1) { if (arg0==null) { arg0 = String.Empty; } if (arg1==null) { arg1 = String.Empty; } return Concat(arg0.ToString(), arg1.ToString()); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat2"]/*' /> publicstatic String Concat(Object arg0, Object arg1, Object arg2) { if (arg0==null) { arg0 = String.Empty; } if (arg1==null) { arg1 = String.Empty; } if (arg2==null) { arg2 = String.Empty; } return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString()); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat3"]/*' /> [CLSCompliant(false)] publicstatic String Concat(Object arg0, Object arg1, Object arg2, Object arg3, __arglist) { Object[] objArgs; int argCount; ArgIterator args =new ArgIterator(__arglist); //+4 to account for the 4 hard-coded arguments at the beginning of the list. argCount = args.GetRemainingCount() +4; objArgs =new Object[argCount]; //Handle the hard-coded arguments objArgs[0] = arg0; objArgs[1] = arg1; objArgs[2] = arg2; objArgs[3] = arg3; //Walk all of the args in the variable part of the argument list. for (int i=4; i<argCount; i++) { objArgs[i] = TypedReference.ToObject(args.GetNextArg()); } return Concat(objArgs); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat4"]/*' /> publicstatic String Concat(params Object[] args) { if (args==null) { thrownew ArgumentNullException("args"); } String[] sArgs =new String[args.Length]; int totalLength=0; for (int i=0; i<args.Length; i++) { sArgs[i] = ((args[i]==null)?(String.Empty):(args[i].ToString())); totalLength += sArgs[i].Length; } return ConcatArray(sArgs, totalLength); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat5"]/*' /> publicstatic String Concat(String str0, String str1) { if (str0 ==null) { if (str1==null) { return String.Empty; } return str1; } if (str1==null) { return str0; } int str0Length = str0.Length; String result = FastAllocateString(str0Length + str1.Length); FillStringChecked(result, 0, str0); FillStringChecked(result, str0Length, str1); return result; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat6"]/*' /> publicstatic String Concat(String str0, String str1, String str2) { if (str0==null&& str1==null&& str2==null) { return String.Empty; } if (str0==null) { str0 = String.Empty; } if (str1==null) { str1 = String.Empty; } if (str2 ==null) { str2 = String.Empty; } int totalLength = str0.Length + str1.Length + str2.Length; String result = FastAllocateString(totalLength); FillStringChecked(result, 0, str0); FillStringChecked(result, str0.Length, str1); FillStringChecked(result, str0.Length + str1.Length, str2); return result; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat7"]/*' /> publicstatic String Concat(String str0, String str1, String str2, String str3) { if (str0==null&& str1==null&& str2==null&& str3==null) { return String.Empty; } if (str0==null) { str0 = String.Empty; } if (str1==null) { str1 = String.Empty; } if (str2 ==null) { str2 = String.Empty; } if (str3 ==null) { str3 = String.Empty; } int totalLength = str0.Length + str1.Length + str2.Length + str3.Length; String result = FastAllocateString(totalLength); FillStringChecked(result, 0, str0); FillStringChecked(result, str0.Length, str1); FillStringChecked(result, str0.Length + str1.Length, str2); FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3); return result; } privatestatic String ConcatArray(String[] values, int totalLength) { String result = FastAllocateString(totalLength); int currPos=0; for (int i=0; i<values.Length; i++) { BCLDebug.Assert((currPos + values[i].Length <= totalLength), "[String.ConcatArray](currPos + values[i].Length <= totalLength)"); FillStringChecked(result, currPos, values[i]); currPos+=values[i].Length; } return result; } privatestatic String[] CopyArrayOnNull(String[] values, outint totalLength) { totalLength =0; String[] outValues =new String[values.Length]; for (int i=0; i<values.Length; i++) { outValues[i] = ((values[i]==null)?String.Empty:values[i]); totalLength += outValues[i].Length; } return outValues; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Concat8"]/*' /> publicstatic String Concat(params String[] values) { int totalLength=0; if (values==null) { thrownew ArgumentNullException("values"); } for (int i=0; i<values.Length; i++) { if (values[i]==null) { values = CopyArrayOnNull(values, out totalLength); break; }else{ totalLength += values[i].Length; } } return ConcatArray(values, totalLength); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.Intern"]/*' /> publicstatic String Intern(String str) { if (str==null) { thrownew ArgumentNullException("str"); } return Thread.GetDomain().GetOrInternString(str); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IsInterned"]/*' /> publicstatic String IsInterned(String str) { if (str==null) { thrownew ArgumentNullException("str"); } return Thread.GetDomain().IsStringInterned(str); } // // IValue implementation // /**////<include file='doc\String.uex' path='docs/doc[@for="String.GetTypeCode"]/*' /> public TypeCode GetTypeCode() { return TypeCode.String; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToBoolean"]/*' /> ///<internalonly/> bool IConvertible.ToBoolean(IFormatProvider provider) { return Convert.ToBoolean(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToChar"]/*' /> ///<internalonly/> char IConvertible.ToChar(IFormatProvider provider) { return Convert.ToChar(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToSByte"]/*' /> ///<internalonly/> [CLSCompliant(false)] sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convert.ToSByte(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToByte"]/*' /> ///<internalonly/> byte IConvertible.ToByte(IFormatProvider provider) { return Convert.ToByte(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToInt16"]/*' /> ///<internalonly/> short IConvertible.ToInt16(IFormatProvider provider) { return Convert.ToInt16(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToUInt16"]/*' /> ///<internalonly/> [CLSCompliant(false)] ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convert.ToUInt16(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToInt32"]/*' /> ///<internalonly/> int IConvertible.ToInt32(IFormatProvider provider) { return Convert.ToInt32(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToUInt32"]/*' /> ///<internalonly/> [CLSCompliant(false)] uint IConvertible.ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToInt64"]/*' /> ///<internalonly/> long IConvertible.ToInt64(IFormatProvider provider) { return Convert.ToInt64(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToUInt64"]/*' /> ///<internalonly/> [CLSCompliant(false)] ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convert.ToUInt64(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToSingle"]/*' /> ///<internalonly/> float IConvertible.ToSingle(IFormatProvider provider) { return Convert.ToSingle(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToDouble"]/*' /> ///<internalonly/> double IConvertible.ToDouble(IFormatProvider provider) { return Convert.ToDouble(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToDecimal"]/*' /> ///<internalonly/> Decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convert.ToDecimal(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToDateTime"]/*' /> ///<internalonly/> DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convert.ToDateTime(this, provider); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IConvertible.ToType"]/*' /> ///<internalonly/> 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)] internalexternbool IsFastSort(); /**////<internalonly/> unsafeinternalvoid SetChar(int index, char value) { #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif //Bounds check and then set the actual bit. if ((UInt32)index >= (UInt32)Length) thrownew ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index")); fixed (char*p =&this.m_firstChar) { // Set the character. p[index] = value; } } #if _DEBUG // Only used in debug build. Insure that the HighChar state information for a string is not set as known [MethodImplAttribute(MethodImplOptions.InternalCall)] privateexternbool ValidModifiableString(); #endif /**////<internalonly/> unsafeinternalvoid AppendInPlace(char value,int currentLength) { BCLDebug.Assert(currentLength < m_arrayLength, "[String.AppendInPlace(char)currentLength < m_arrayLength"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif fixed (char*p =&this.m_firstChar) { // Append the character. p[currentLength] = value; p[++currentLength] ='\0'; m_stringLength = currentLength; } } /**////<internalonly/> unsafeinternalvoid AppendInPlace(char value, int repeatCount, int currentLength) { BCLDebug.Assert(currentLength+repeatCount < m_arrayLength, "[String.AppendInPlace]currentLength+repeatCount < m_arrayLength"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif int newLength = currentLength + repeatCount; fixed (char*p =&this.m_firstChar) { int i; for (i=currentLength; i<newLength; i++) { p[i] = value; } p[i] ='\0'; } this.m_stringLength = newLength; } /**////<internalonly/> internalunsafevoid AppendInPlace(String value, int currentLength) { BCLDebug.Assert(value!=null, "[String.AppendInPlace]value!=null"); BCLDebug.Assert(value.Length + currentLength <this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif FillString(this, currentLength, value); SetLength(currentLength + value.Length); NullTerminate(); } internalvoid AppendInPlace(String value, int startIndex, int count, int currentLength) { BCLDebug.Assert(value!=null, "[String.AppendInPlace]value!=null"); BCLDebug.Assert(count + currentLength <this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); BCLDebug.Assert(count>=0, "[String.AppendInPlace]count>=0"); BCLDebug.Assert(startIndex>=0, "[String.AppendInPlace]startIndex>=0"); BCLDebug.Assert(startIndex <= (value.Length - count), "[String.AppendInPlace]startIndex <= (value.Length - count)"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif FillSubstring(this, currentLength, value, startIndex, count); SetLength(currentLength + count); NullTerminate(); } internalunsafevoid AppendInPlace(char*value, int count,int currentLength) { BCLDebug.Assert(value!=null, "[String.AppendInPlace]value!=null"); BCLDebug.Assert(count + currentLength <this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); BCLDebug.Assert(count>=0, "[String.AppendInPlace]count>=0"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif fixed(char*p =&this.m_firstChar) { int i; for (i=0; i<count; i++) { p[currentLength+i] = value[i]; } } SetLength(currentLength + count); NullTerminate(); } /**////<internalonly/> internalunsafevoid AppendInPlace(char[] value, int start, int count, int currentLength) { BCLDebug.Assert(value!=null, "[String.AppendInPlace]value!=null"); BCLDebug.Assert(count + currentLength <this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); BCLDebug.Assert(value.Length-count>=start, "[String.AppendInPlace]value.Length-count>=start"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif FillStringArray(this, currentLength, value, start, count); this.m_stringLength = (currentLength + count); this.NullTerminate(); } /**////<internalonly/> unsafeinternalvoid ReplaceCharInPlace(char oldChar, char newChar, int startIndex, int count,int currentLength) { BCLDebug.Assert(startIndex>=0, "[String.ReplaceCharInPlace]startIndex>0"); BCLDebug.Assert(startIndex<=currentLength, "[String.ReplaceCharInPlace]startIndex>=Length"); BCLDebug.Assert((startIndex<=currentLength-count), "[String.ReplaceCharInPlace]count>0 && startIndex<=currentLength-count"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif int endIndex = startIndex+count; fixed (char*p =&this.m_firstChar) { for (int i=startIndex;i<endIndex; i++) { if (p[i]==oldChar) { p[i]=newChar; } } } } /**////<internalonly/> internalstatic String GetStringForStringBuilder(String value, int capacity) { BCLDebug.Assert(value!=null, "[String.GetStringForStringBuilder]value!=null"); BCLDebug.Assert(capacity>=value.Length, "[String.GetStringForStringBuilder]capacity>=value.Length"); String newStr = FastAllocateString(capacity); if (value.Length==0) { newStr.m_stringLength=0; newStr.m_firstChar='\0'; return newStr; } FillString(newStr, 0, value); newStr.m_stringLength = value.m_stringLength; return newStr; } /**////<internalonly/> privateunsafevoid NullTerminate() { fixed(char*p =&this.m_firstChar) { p[m_stringLength] ='\0'; } } /**////<internalonly/> unsafeinternalvoid ClearPostNullChar() { int newLength = Length+1; if (newLength<m_arrayLength) { fixed(char*p =&this.m_firstChar) { p[newLength] ='\0'; } } } /**////<internalonly/> internalvoid SetLength(int newLength) { BCLDebug.Assert(newLength <= m_arrayLength, "newLength<=m_arrayLength"); m_stringLength = newLength; } /**////<include file='doc\String.uex' path='docs/doc[@for="String.GetEnumerator"]/*' /> public CharEnumerator GetEnumerator() { BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead."); returnnew CharEnumerator(this); } /**////<include file='doc\String.uex' path='docs/doc[@for="String.IEnumerable.GetEnumerator"]/*' /> ///<internalonly/> IEnumerator IEnumerable.GetEnumerator() { BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead."); returnnew CharEnumerator(this); } // // This is just designed to prevent compiler warnings. // This field is used from native, but we need to prevent the compiler warnings. // #if _DEBUG privatevoid DontTouchThis() { m_arrayLength =0; m_stringLength =0; m_firstChar = m_firstChar; } #endif internalunsafevoid InternalSetCharNoBoundsCheck(int index, char value) { fixed (char*p =&this.m_firstChar) { p[index] = value; } } // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes. internalunsafestaticvoid 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.ToPointer(); System.IO.__UnmanagedMemoryStream.memcpyimpl(srcPtr, dstPtr, len); } } // memcopies characters inside a String. internalunsafestaticvoid InternalMemCpy(String src, int srcOffset, String dst, int destOffset, int len) { if (len ==0) return; fixed(char* srcPtr =&src.m_firstChar) { fixed(char* dstPtr =&dst.m_firstChar) { System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)(srcPtr + srcOffset), (byte*)(dstPtr + destOffset), len); } } } internalunsafestaticvoid revmemcpyimpl(byte* src, byte* dest, int len) { if (len ==0) return; dest += len; src += len; if (((long)src &3) !=0) { do{ dest--; src--; len--; *dest =*src; }while (len >0&& ((long)src &3) !=0); } if (len >=16){ len -=16; do{ dest -= (byte*)16; src -= (byte*)16; ((int*)dest)[3] = ((int*)src)[3]; ((int*)dest)[2] = ((int*)src)[2]; ((int*)dest)[1] = ((int*)src)[1]; ((int*)dest)[0] = ((int*)src)[0]; }while ((len -=16) >=0); } if ((len &8) >0) { dest -= (byte*)8; src -= (byte*)8; ((int*)dest)[1] = ((int*)src)[1]; ((int*)dest)[0] = ((int*)src)[0]; } if ((len &4) >0) { dest -= (byte*)4; src -= (byte*)4; ((int*)dest)[0] = ((int*)src)[0]; } if ((len &2) !=0) { dest -= (byte*)2; src -= (byte*)2; ((short*)dest)[0] = ((short*)src)[0]; } if ((len &1) !=0) { dest--; src--; *dest =*src; } } // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes. internalunsafestaticvoid InternalCopy(String src, byte[] dest,int len) { if (len ==0) return; fixed(char* charPtr =&src.m_firstChar) { fixed(byte* destPtr = dest) { byte* srcPtr = (byte*) charPtr; System.IO.__UnmanagedMemoryStream.memcpyimpl(srcPtr, destPtr, len); } } } internalunsafevoid InsertInPlace(int index, String value, int repeatCount, int currentLength, int requiredLength) { BCLDebug.Assert(requiredLength < m_arrayLength, "[String.InsertString] requiredLength < m_arrayLength"); BCLDebug.Assert(index + value.Length * repeatCount < m_arrayLength, "[String.InsertString] index + value.Length * repeatCount < m_arrayLength"); #if _DEBUG BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); #endif //Copy the old characters over to make room and then insert the new characters. fixed(char* srcPtr =&this.m_firstChar) { fixed(char* valuePtr =&value.m_firstChar) { revmemcpyimpl((byte*)(srcPtr + index),(byte*)(srcPtr + index + value.Length * repeatCount), (currentLength - index) *sizeof(char)); for (int i=0; i<repeatCount; i++) { System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)valuePtr, (byte*)(srcPtr + index + i * value.Length), value.Length *sizeof(char)); } } srcPtr[requiredLength] ='\0'; } this.m_stringLength = requiredLength; } } }
posted on
2005-08-04 01:03wanna
阅读(420)
评论(0)
编辑收藏举报