- // ==++==
- //
- //
- // 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] public sealed class 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]private int m_arrayLength;
- [NonSerialized]private int m_stringLength;
- [NonSerialized]private char 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.
- private const int TrimHead = 0;
- private const int TrimTail = 1;
- private const int 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"]/*' />
- public static readonly 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"]/*' />
- public static String Join (String separator, String[] value) {
- if (value==null) {
- throw new 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)]
- public static extern String Join (String separator, String[] value, int startIndex, int count);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern int nativeCompareOrdinal(String strA, String strB, bool bIgnoreCase);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern int 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)]
- unsafe internal static extern int nativeCompareOrdinalWC(String strA, char *strBChars, bool bIgnoreCase, out bool 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.
- //
- internal static String SmallCharToUpper(String strA) {
- String newString = FastAllocateString(strA.Length);
- nativeSmallCharToUpper(strA, newString);
- return newString;
- }
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private static extern void 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.
- internal static 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)]
- public extern override bool Equals(Object obj);
- // Determines whether two strings match.
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Equals1"]/*' />
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern bool Equals(String value);
- // Determines whether two Strings match.
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Equals2"]/*' />
- public static bool Equals(String a, String b) {
- if ((Object)a==(Object)b) {
- return true;
- }
- if ((Object)a==null || (Object)b==null) {
- return false;
- }
- return a.Equals(b);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.operatorEQ"]/*' />
- public static bool operator == (String a, String b) {
- return String.Equals(a, b);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.operatorNE"]/*' />
- public static bool operator != (String a, String b) {
- return !String.Equals(a, b);
- }
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern char 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")]
- public char this[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"]/*' />
- 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"));
- InternalCopyTo(sourceIndex, destination, destinationIndex, count);
- }
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern void InternalCopyTo(int sourceIndex, char[] destination, int destinationIndex, int count);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern void 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"]/*' />
- public char[] 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"]/*' />
- 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"));
- char[] chars = new char[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)]
- public extern override int GetHashCode();
- // Gets the length of this string
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Length"]/*' />
- public int 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)]
- private extern int InternalLength();
- ///<internalonly/>
- internal int ArrayLength {
- get { return (m_arrayLength); }
- }
- // Used by StringBuilder
- internal int 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(params char [] 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)]
- public extern 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) {
- return this.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) {
- throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
- }
- if (length<0) {
- throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
- }
- if (startIndex > thisLength-length) {
- throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_IndexLength"));
- }
- String s = FastAllocateString(length);
- FillSubstring(s, 0, this, startIndex, length);
- return s;
- }
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern 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.
- internal static readonly char[] 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(params char[] 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(params char[] 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(params char[] 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)]
- unsafe public extern String(char *value);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String1"]/*' />
- [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
- unsafe public extern String(char *value, int startIndex, int length);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String2"]/*' />
- [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
- unsafe public extern String(sbyte *value);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String3"]/*' />
- [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
- unsafe public extern String(sbyte *value, int startIndex, int length);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String4"]/*' />
- [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("length",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- byte [] b = new byte[length];
- __UnmanagedMemoryStream.memcpy((byte*)value, startIndex, b, 0, length);
- return enc.GetString(b);
- }
- // For ASCIIEncoding::GetString()
- unsafe static internal 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)]
- private extern static String FastAllocateString(int length);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void FillString(String dest, int destPos, String src);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void FillStringChecked(String dest, int destPos, String src);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void FillStringEx(String dest, int destPos, String src,int srcLength);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void FillStringArray(String dest, int stringStart, char[] array, int charStart, int count);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void 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)]
- 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.
- //
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String5"]/*' />
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern String(char [] value);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.String6"]/*' />
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern 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"]/*' />
- public static int 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"]/*' />
- public static int 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"]/*' />
- public static int Compare(String strA, String strB, bool ignoreCase, CultureInfo culture) {
- if (culture==null) {
- throw new 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"]/*' />
- public static int 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"]/*' />
- public static int 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"]/*' />
- public static int Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture) {
- if (culture==null) {
- throw new 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"]/*' />
- public int CompareTo(Object value) {
- if (value == null) {
- return 1;
- }
- if (!(value is String)) {
- throw new 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"]/*' />
- public int CompareTo(String strB) {
- if (strB==null) {
- return 1;
- }
- 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"]/*' />
- public static int CompareOrdinal(String strA, String strB) {
- if (strA == null || strB == null) {
- if ((Object)strA==(Object)strB) { //they're both null;
- return 0;
- }
- 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"]/*' />
- public static int 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;
- return 0;
- }
- 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"]/*' />
- public bool EndsWith(String value) {
- if (null==value) {
- throw new ArgumentNullException("value");
- }
- int valueLen = value.Length;
- int thisLen = this.Length;
- if (valueLen>thisLen) {
- return false;
- }
- return (0==Compare(this, thisLen-valueLen, value, 0, valueLen));
- }
- internal bool EndsWith(char value) {
- int thisLen = this.Length;
- if (thisLen != 0) {
- if (this[thisLen - 1] == value)
- return true;
- }
- return false;
- }
- // 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"]/*' />
- public int IndexOf(char value) {
- return IndexOf(value, 0, this.Length);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.IndexOf1"]/*' />
- public int 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)]
- public extern int 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"]/*' />
- public int IndexOfAny(char [] anyOf) {
- return IndexOfAny(anyOf,0, this.Length);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.IndexOfAny2"]/*' />
- public int 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)]
- public extern int 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"]/*' />
- public int 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"]/*' />
- public int 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"]/*' />
- public int IndexOf(String value, int startIndex, int count){
- if (startIndex + count > this.Length) {
- throw new 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"]/*' />
- public int LastIndexOf(char value) {
- return LastIndexOf(value, this.Length-1, this.Length);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.LastIndexOf1"]/*' />
- public int 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)]
- public extern int 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"]/*' />
- public int LastIndexOfAny(char [] anyOf) {
- return LastIndexOfAny(anyOf,this.Length-1,this.Length);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.LastIndexOfAny2"]/*' />
- public int 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)]
- public extern int 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"]/*' />
- public int LastIndexOf(String value) {
- return LastIndexOf(value, this.Length-1,this.Length);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.LastIndexOf7"]/*' />
- public int LastIndexOf(String value, int startIndex) {
- return LastIndexOf(value, startIndex, startIndex + 1);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.LastIndexOf8"]/*' />
- public int LastIndexOf(String value, int startIndex, int count) {
- if (count<0) {
- throw new 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)]
- private extern 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"]/*' />
- public bool StartsWith(String value) {
- if (null==value) {
- throw new ArgumentNullException("value");
- }
- if (this.Length<value.Length) {
- return false;
- }
- 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() {
- return this.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) {
- throw new 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() {
- return this.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) {
- throw new ArgumentNullException("culture");
- }
- return culture.TextInfo.ToUpper(this);
- }
- // Returns this string.
- /// <include file='doc/String.uex' path='docs/doc[@for="String.ToString"]/*' />
- public override String ToString() {
- return this;
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.ToString1"]/*' />
- 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.
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Clone"]/*' />
- public Object Clone() {
- return this;
- }
- // 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() {
- return this.Trim(WhitespaceChars);
- }
- //
- //
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Insert"]/*' />
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern 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)]
- public extern 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)]
- public extern String Replace (String oldValue, String newValue);
- //
- //
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Remove"]/*' />
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern String Remove(int startIndex, int count);
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Format"]/*' />
- public static String Format(String format, Object arg0) {
- return Format(null, format, new Object[] {arg0});
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Format1"]/*' />
- public static 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"]/*' />
- public static 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"]/*' />
- public static String Format(String format, params Object[] args) {
- return Format(null, format, args);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Format4"]/*' />
- public static String Format( IFormatProvider provider, String format, params Object[] args) {
- if (format == null || args == null)
- throw new 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"]/*' />
- public static String Copy (String str) {
- if (str==null) {
- throw new ArgumentNullException("str");
- }
- int length = str.Length;
- String result = FastAllocateString(length);
- FillString(result, 0, str);
- return result;
- }
- // Used by StringBuilder to avoid data corruption
- internal static 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"]/*' />
- public static String Concat(Object arg0) {
- if (arg0==null) {
- return String.Empty;
- }
- return arg0.ToString();
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.Concat1"]/*' />
- public static 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"]/*' />
- public static 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)]
- public static 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"]/*' />
- public static String Concat(params Object[] args) {
- if (args==null) {
- throw new 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"]/*' />
- public static 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"]/*' />
- public static 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"]/*' />
- public static 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;
- }
- private static 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;
- }
- private static String[] CopyArrayOnNull(String[] values, out int 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"]/*' />
- public static String Concat(params String[] values) {
- int totalLength=0;
- if (values==null) {
- throw new 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"]/*' />
- public static String Intern(String str) {
- if (str==null) {
- throw new ArgumentNullException("str");
- }
- return Thread.GetDomain().GetOrInternString(str);
- }
- /// <include file='doc/String.uex' path='docs/doc[@for="String.IsInterned"]/*' />
- public static String IsInterned(String str) {
- if (str==null) {
- throw new 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)]
- internal extern bool IsFastSort();
- ///<internalonly/>
- unsafe internal void 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)
- throw new 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)]
- private extern bool ValidModifiableString();
- #endif
- ///<internalonly/>
- unsafe internal void 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/>
- unsafe internal void 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/>
- internal unsafe void 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();
- }
- internal void 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();
- }
- internal unsafe void 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/>
- internal unsafe void 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/>
- unsafe internal void 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/>
- internal static 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/>
- private unsafe void NullTerminate() {
- fixed(char *p = this.m_firstChar) {
- p[m_stringLength] = '/0';
- }
- }
- ///<internalonly/>
- unsafe internal void ClearPostNullChar() {
- int newLength = Length+1;
- if (newLength<m_arrayLength) {
- fixed(char *p = this.m_firstChar) {
- p[newLength] = '/0';
- }
- }
- }
- ///<internalonly/>
- internal void 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.");
- return new 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.");
- return new 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
- private void DontTouchThis() {
- m_arrayLength = 0;
- m_stringLength = 0;
- m_firstChar = m_firstChar;
- }
- #endif
- internal unsafe void 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.
- 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.ToPointer();
- System.IO.__UnmanagedMemoryStream.memcpyimpl(srcPtr, dstPtr, len);
- }
- }
- // memcopies characters inside a String.
- internal unsafe static void 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);
- }
- }
- }
- internal unsafe static void 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.
- internal unsafe static void 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);
- }
- }
- }
- internal unsafe void 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;
- }
- }
- }
String
最新推荐文章于 2021-02-04 17:48:34 发布