The Labs \ Source Viewer \ SSCLI \ System \ String

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  String
  18. **
  19. **
  20. ** Purpose: Contains headers for the String class.  Actual implementations
  21. ** are in String.cpp
  22. **
  23. **
  24. ===========================================================*/
  25. namespace System
  26. {
  27.     using System.Text;
  28.     using System;
  29.     using System.Runtime.ConstrainedExecution;
  30.     using System.Globalization;
  31.     using System.Threading;
  32.     using System.Collections;
  33.     using System.Collections.Generic;
  34.     using System.Runtime.CompilerServices;
  35.     using Microsoft.Win32;
  36.     using System.Runtime.InteropServices;
  37.     using va_list = System.ArgIterator;
  38.     //
  39.     // For Information on these methods, please see COMString.cpp
  40.     //
  41.     // The String class represents a static string of characters. Many of
  42.     // the String methods perform some type of transformation on the current
  43.     // instance and return the result as a new String. All comparison methods are
  44.     // implemented as a part of String. As with arrays, character positions
  45.     // (indices) are zero-based.
  46.     //
  47.     // When passing a null string into a constructor in VJ and VC, the null should be
  48.     // explicitly type cast to a String.
  49.     // For Example:
  50.     // String s = new String((String)null);
  51.     // Text.Out.WriteLine(s);
  52.     //
  53.     [System.Runtime.InteropServices.ComVisible(true)]
  54.     [Serializable()]
  55.     public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string>
  56.     {
  57.        
  58.         //
  59.         //NOTE NOTE NOTE NOTE
  60.         //These fields map directly onto the fields in an EE StringObject. See object.h for the layout.
  61.         //
  62.         [NonSerialized()]
  63.         private int m_arrayLength;
  64.         [NonSerialized()]
  65.         private int m_stringLength;
  66.         [NonSerialized()]
  67.         private char m_firstChar;
  68.        
  69.         //private static readonly char FmtMsgMarkerChar='%';
  70.         //private static readonly char FmtMsgFmtCodeChar='!';
  71.         //These are defined in Com99/src/vm/COMStringCommon.h and must be kept in sync.
  72.         private const int TrimHead = 0;
  73.         private const int TrimTail = 1;
  74.         private const int TrimBoth = 2;
  75.        
  76.         // The Empty constant holds the empty string value.
  77.         //We need to call the String constructor so that the compiler doesn't mark this as a literal.
  78.         //Marking this as a literal would mean that it doesn't show up as a field which we can access
  79.         //from native.
  80.         public static readonly string Empty = "";
  81.        
  82.         //
  83.         //Native Static Methods
  84.         //
  85.        
  86.         // Joins an array of strings together as one string with a separator between each original string.
  87.         //
  88.         public static string Join(string separator, string[] value)
  89.         {
  90.             if (value == null) {
  91.                 throw new ArgumentNullException("value");
  92.             }
  93.             return Join(separator, value, 0, value.Length);
  94.         }
  95.        
  96.         private const int charPtrAlignConst = 1;
  97.         private const int alignConst = 3;
  98.        
  99.         internal char FirstChar {
  100.             get { return m_firstChar; }
  101.         }
  102.        
  103.         // Joins an array of strings together as one string with a separator between each original string.
  104.         //
  105.         unsafe public static string Join(string separator, string[] value, int startIndex, int count)
  106.         {
  107.             //Treat null as empty string.
  108.             if (separator == null) {
  109.                 separator = String.Empty;
  110.             }
  111.            
  112.             //Range check the array
  113.             if (value == null) {
  114.                 throw new ArgumentNullException("value");
  115.             }
  116.            
  117.             if (startIndex < 0) {
  118.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  119.             }
  120.             if (count < 0) {
  121.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
  122.             }
  123.            
  124.             if (startIndex > value.Length - count) {
  125.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
  126.             }
  127.            
  128.             //If count is 0, that skews a whole bunch of the calculations below, so just special case that.
  129.             if (count == 0) {
  130.                 return String.Empty;
  131.             }
  132.            
  133.             int jointLength = 0;
  134.             //Figure out the total length of the strings in value
  135.             int endIndex = startIndex + count - 1;
  136.             for (int stringToJoinIndex = startIndex; stringToJoinIndex <= endIndex; stringToJoinIndex++) {
  137.                 if (value[stringToJoinIndex] != null) {
  138.                     jointLength += value[stringToJoinIndex].Length;
  139.                 }
  140.             }
  141.            
  142.             //Add enough room for the separator.
  143.             jointLength += (count - 1) * separator.Length;
  144.            
  145.             // Note that we may not catch all overflows with this check (since we could have wrapped around the 4gb range any number of times
  146.             // and landed back in the positive range.) The input array might be modifed from other threads,
  147.             // so we have to do an overflow check before each append below anyway. Those overflows will get caught down there.
  148.             if ((jointLength < 0) || ((jointLength + 1) < 0)) {
  149.                 throw new OutOfMemoryException();
  150.             }
  151.            
  152.             //If this is an empty string, just return.
  153.             if (jointLength == 0) {
  154.                 return String.Empty;
  155.             }
  156.            
  157.             string jointString = FastAllocateString(jointLength);
  158.             fixed (char* pointerToJointString = &jointString.m_firstChar) {
  159.                 UnSafeCharBuffer charBuffer = new UnSafeCharBuffer(pointerToJointString, jointLength);
  160.                
  161.                 // Append the first string first and then append each following string prefixed by the separator.
  162.                 charBuffer.AppendString(value[startIndex]);
  163.                 for (int stringToJoinIndex = startIndex + 1; stringToJoinIndex <= endIndex; stringToJoinIndex++) {
  164.                     charBuffer.AppendString(separator);
  165.                     charBuffer.AppendString(value[stringToJoinIndex]);
  166.                 }
  167.                 BCLDebug.Assert(*(pointerToJointString + charBuffer.Length) == '\0', "String must be null-terminated!");
  168.             }
  169.            
  170.             return jointString;
  171.         }
  172.        
  173.        
  174.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  175.         static internal extern int nativeCompareOrdinal(string strA, string strB, bool bIgnoreCase);
  176.        
  177.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  178.         static internal extern int nativeCompareOrdinalEx(string strA, int indexA, string strB, int indexB, int count);
  179.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  180.         unsafe static internal extern int nativeCompareOrdinalWC(string strA, char* strBChars, bool bIgnoreCase, out bool success);
  181.         //
  182.         // This is a helper method for the security team. They need to uppercase some strings (guaranteed to be less
  183.         // than 0x80) before security is fully initialized. Without security initialized, we can't grab resources (the nlp's)
  184.         // from the assembly. This provides a workaround for that problem and should NOT be used anywhere else.
  185.         //
  186.         unsafe static internal string SmallCharToUpper(string strIn)
  187.         {
  188.             BCLDebug.Assert(strIn != null, "strIn");
  189.             //
  190.             // Get the length and pointers to each of the buffers. Walk the length
  191.             // of the string and copy the characters from the inBuffer to the outBuffer,
  192.             // capitalizing it if necessary. We assert that all of our characters are
  193.             // less than 0x80.
  194.             //
  195.             int length = strIn.Length;
  196.             string strOut = FastAllocateString(length);
  197.             fixed (char* inBuff = &strIn.m_firstChar, outBuff = &strOut.m_firstChar) {
  198.                 char c;
  199.                
  200.                 int upMask = ~32;
  201.                 for (int i = 0; i < length; i++) {
  202.                     c = inBuff[i];
  203.                     BCLDebug.Assert((int)c < 128, "(int)c < 0x80");
  204.                    
  205.                     //
  206.                     // 0x20 is the difference between upper and lower characters in the lower
  207.                     // 128 ASCII characters. And this bit off to make the chars uppercase.
  208.                     //
  209.                     if (c >= 'a' && c <= 'z') {
  210.                         c = (char)((int)c & upMask);
  211.                     }
  212.                     outBuff[i] = c;
  213.                 }
  214.                
  215.                 BCLDebug.Assert(outBuff[length] == '\0', "outBuff[length]=='\0'");
  216.             }
  217.             return strOut;
  218.         }
  219.        
  220.         //
  221.         //
  222.         // NATIVE INSTANCE METHODS
  223.         //
  224.         //
  225.        
  226.         //
  227.         // Search/Query methods
  228.         //
  229.        
  230.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  231.         unsafe private static bool EqualsHelper(string strA, string strB)
  232.         {
  233.             int length = strA.Length;
  234.             if (length != strB.Length)
  235.                 return false;
  236.            
  237.             fixed (char* ap = strA)
  238.                 fixed (char* bp = strB) {
  239.                     char* a = ap;
  240.                     char* b = bp;
  241.                    
  242.                     // unroll the loop
  243.                     while (length >= 10) {
  244.                         if (*(int*)a != *(int*)b)
  245.                             break;
  246.                         if (*(int*)(a + 2) != *(int*)(b + 2))
  247.                             break;
  248.                         if (*(int*)(a + 4) != *(int*)(b + 4))
  249.                             break;
  250.                         if (*(int*)(a + 6) != *(int*)(b + 6))
  251.                             break;
  252.                         if (*(int*)(a + 8) != *(int*)(b + 8))
  253.                             break;
  254.                         a += 10;
  255.                         b += 10;
  256.                         length -= 10;
  257.                     }
  258.                    
  259.                     // This depends on the fact that the String objects are
  260.                     // always zero terminated and that the terminating zero is not included
  261.                     // in the length. For odd string sizes, the last compare will include
  262.                     // the zero terminator.
  263.                     while (length > 0) {
  264.                         if (*(int*)a != *(int*)b)
  265.                             break;
  266.                         a += 2;
  267.                         b += 2;
  268.                         length -= 2;
  269.                     }
  270.                    
  271.                     return (length <= 0);
  272.                 }
  273.         }
  274.        
  275.         unsafe private static int CompareOrdinalHelper(string strA, string strB)
  276.         {
  277.             BCLDebug.Assert(strA != null && strB != null, "strings cannot be null!");
  278.             int length = Math.Min(strA.Length, strB.Length);
  279.             int diffOffset = -1;
  280.            
  281.             fixed (char* ap = strA)
  282.                 fixed (char* bp = strB) {
  283.                     char* a = ap;
  284.                     char* b = bp;
  285.                    
  286.                     // unroll the loop
  287.                     while (length >= 10) {
  288.                         if (*(int*)a != *(int*)b) {
  289.                             diffOffset = 0;
  290.                             break;
  291.                         }
  292.                        
  293.                         if (*(int*)(a + 2) != *(int*)(b + 2)) {
  294.                             diffOffset = 2;
  295.                             break;
  296.                         }
  297.                        
  298.                         if (*(int*)(a + 4) != *(int*)(b + 4)) {
  299.                             diffOffset = 4;
  300.                             break;
  301.                         }
  302.                        
  303.                         if (*(int*)(a + 6) != *(int*)(b + 6)) {
  304.                             diffOffset = 6;
  305.                             break;
  306.                         }
  307.                        
  308.                         if (*(int*)(a + 8) != *(int*)(b + 8)) {
  309.                             diffOffset = 8;
  310.                             break;
  311.                         }
  312.                         a += 10;
  313.                         b += 10;
  314.                         length -= 10;
  315.                     }
  316.                    
  317.                     if (diffOffset != -1) {
  318.                         // we already see a difference in the unrolled loop above
  319.                         a += diffOffset;
  320.                         b += diffOffset;
  321.                         int order;
  322.                         if ((order = (int)*a - (int)*b) != 0) {
  323.                             return order;
  324.                         }
  325.                         BCLDebug.Assert(*(a + 1) != *(b + 1), "This byte must be different if we reach here!");
  326.                         return ((int)*(a + 1) - (int)*(b + 1));
  327.                     }
  328.                    
  329.                     // now go back to slower code path and do comparison on 4 bytes one time.
  330.                     // Following code also take advantage of the fact strings will
  331.                     // use even numbers of characters (runtime will have a extra zero at the end.)
  332.                     // so even if length is 1 here, we can still do the comparsion.
  333.                     while (length > 0) {
  334.                         if (*(int*)a != *(int*)b) {
  335.                             break;
  336.                         }
  337.                         a += 2;
  338.                         b += 2;
  339.                         length -= 2;
  340.                     }
  341.                    
  342.                     if (length > 0) {
  343.                         int c;
  344.                         // found a different int on above loop
  345.                         if ((c = (int)*a - (int)*b) != 0) {
  346.                             return c;
  347.                         }
  348.                         BCLDebug.Assert(*(a + 1) != *(b + 1), "This byte must be different if we reach here!");
  349.                         return ((int)*(a + 1) - (int)*(b + 1));
  350.                     }
  351.                    
  352.                     // At this point, we have compared all the characters in at least one string.
  353.                     // The longer string will be larger.
  354.                     return strA.Length - strB.Length;
  355.                 }
  356.         }
  357.        
  358.         // Determines whether two strings match.
  359.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  360.         public override bool Equals(object obj)
  361.         {
  362.             string str = obj as string;
  363.             if (str == null) {
  364.                 // exception will be thrown later for null this
  365.                 if (this != null)
  366.                     return false;
  367.             }
  368.            
  369.             return EqualsHelper(this, str);
  370.         }
  371.        
  372.         // Determines whether two strings match.
  373.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  374.         public bool Equals(string value)
  375.         {
  376.             if (value == null) {
  377.                 // exception will be thrown later for null this
  378.                 if (this != null)
  379.                     return false;
  380.             }
  381.            
  382.             return EqualsHelper(this, value);
  383.         }
  384.        
  385.         public bool Equals(string value, StringComparison comparisonType)
  386.         {
  387.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  388.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  389.             }
  390.            
  391.             if ((object)this == (object)value) {
  392.                 return true;
  393.             }
  394.            
  395.             if ((object)value == null) {
  396.                 return false;
  397.             }
  398.            
  399.             switch (comparisonType) {
  400.                 case StringComparison.CurrentCulture:
  401.                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0);
  402.                 case StringComparison.CurrentCultureIgnoreCase:
  403.                    
  404.                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0);
  405.                 case StringComparison.InvariantCulture:
  406.                    
  407.                     return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0);
  408.                 case StringComparison.InvariantCultureIgnoreCase:
  409.                    
  410.                     return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0);
  411.                 case StringComparison.Ordinal:
  412.                    
  413.                     return this.Equals(value);
  414.                 case StringComparison.OrdinalIgnoreCase:
  415.                    
  416.                     if (this.Length != value.Length)
  417.                         return false;
  418.                     else {
  419.                         // If both strings are ASCII strings, we can take the fast path.
  420.                         if (this.IsAscii() && value.IsAscii()) {
  421.                             return (String.nativeCompareOrdinal(this, value, true) == 0);
  422.                         }
  423.                         // Take the slow path.
  424.                         return (TextInfo.CompareOrdinalIgnoreCase(this, value) == 0);
  425.                     }
  426.                     break;
  427.                 default:
  428.                    
  429.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  430.                     break;
  431.             }
  432.         }
  433.        
  434.        
  435.         // Determines whether two Strings match.
  436.         public static bool Equals(string a, string b)
  437.         {
  438.             if ((object)a == (object)b) {
  439.                 return true;
  440.             }
  441.            
  442.             if ((object)a == null || (object)b == null) {
  443.                 return false;
  444.             }
  445.            
  446.             return EqualsHelper(a, b);
  447.         }
  448.        
  449.         public static bool Equals(string a, string b, StringComparison comparisonType)
  450.         {
  451.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  452.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  453.             }
  454.            
  455.             if ((object)a == (object)b) {
  456.                 return true;
  457.             }
  458.            
  459.             if ((object)a == null || (object)b == null) {
  460.                 return false;
  461.             }
  462.            
  463.             switch (comparisonType) {
  464.                 case StringComparison.CurrentCulture:
  465.                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0);
  466.                 case StringComparison.CurrentCultureIgnoreCase:
  467.                    
  468.                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0);
  469.                 case StringComparison.InvariantCulture:
  470.                    
  471.                     return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0);
  472.                 case StringComparison.InvariantCultureIgnoreCase:
  473.                    
  474.                     return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0);
  475.                 case StringComparison.Ordinal:
  476.                    
  477.                     return EqualsHelper(a, b);
  478.                 case StringComparison.OrdinalIgnoreCase:
  479.                    
  480.                     if (a.Length != b.Length)
  481.                         return false;
  482.                     else {
  483.                         // If both strings are ASCII strings, we can take the fast path.
  484.                         if (a.IsAscii() && b.IsAscii()) {
  485.                             return (String.nativeCompareOrdinal(a, b, true) == 0);
  486.                         }
  487.                         // Take the slow path.
  488.                        
  489.                         return (TextInfo.CompareOrdinalIgnoreCase(a, b) == 0);
  490.                     }
  491.                     break;
  492.                 default:
  493.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  494.                     break;
  495.             }
  496.         }
  497.        
  498.         public static bool operator ==(string a, string b)
  499.         {
  500.             return String.Equals(a, b);
  501.         }
  502.        
  503.         public static bool operator !=(string a, string b)
  504.         {
  505.             return !String.Equals(a, b);
  506.         }
  507.        
  508.         // Gets the character at a specified position.
  509.         //
  510.         [System.Runtime.CompilerServices.IndexerName("Chars")]
  511.         public extern char this[int index]
  512.         {
  513.             [MethodImpl(MethodImplOptions.InternalCall)]
  514.             get;
  515.         }
  516.        
  517.         // Converts a substring of this string to an array of characters. Copies the
  518.         // characters of this string beginning at position startIndex and ending at
  519.         // startIndex + length - 1 to the character array buffer, beginning
  520.         // at bufferStartIndex.
  521.         //
  522.         unsafe public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
  523.         {
  524.             if (destination == null)
  525.                 throw new ArgumentNullException("destination");
  526.             if (count < 0)
  527.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
  528.             if (sourceIndex < 0)
  529.                 throw new ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  530.             if (count > Length - sourceIndex)
  531.                 throw new ArgumentOutOfRangeException("sourceIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount"));
  532.             if (destinationIndex > destination.Length - count || destinationIndex < 0)
  533.                 throw new ArgumentOutOfRangeException("destinationIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCount"));
  534.            
  535.             // Note: fixed does not like empty arrays
  536.             if (count > 0) {
  537.                 fixed (char* src = &this.m_firstChar)
  538.                     fixed (char* dest = destination)
  539.                         wstrcpy(dest + destinationIndex, src + sourceIndex, count);
  540.             }
  541.         }
  542.        
  543.         // Returns the entire string as an array of characters.
  544.         unsafe public char[] ToCharArray()
  545.         {
  546.             int length = Length;
  547.             char[] chars = new char[length];
  548.             if (length > 0) {
  549.                 fixed (char* src = &this.m_firstChar)
  550.                     fixed (char* dest = chars)
  551.                         wstrcpyPtrAligned(dest, src, length);
  552.             }
  553.             return chars;
  554.         }
  555.        
  556.         // Returns a substring of this string as an array of characters.
  557.         //
  558.         unsafe public char[] ToCharArray(int startIndex, int length)
  559.         {
  560.             // Range check everything.
  561.             if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
  562.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  563.             if (length < 0)
  564.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  565.            
  566.             char[] chars = new char[length];
  567.             if (length > 0) {
  568.                 fixed (char* src = &this.m_firstChar)
  569.                     fixed (char* dest = chars) {
  570.                         wstrcpy(dest, src + startIndex, length);
  571.                     }
  572.             }
  573.             return chars;
  574.         }
  575.        
  576.         public static bool IsNullOrEmpty(string value)
  577.         {
  578.             return (value == null || value.Length == 0);
  579.         }
  580.        
  581.         // Gets a hash code for this string. If strings A and B are such that A.Equals(B), then
  582.         // they will return the same hash code.
  583.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  584.         public override int GetHashCode()
  585.         {
  586.             unsafe {
  587.                 fixed (char* src = this) {
  588.                     BCLDebug.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'");
  589.                     BCLDebug.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary");
  590.                    
  591.                     #if WIN32
  592.                     int hash1 = (5381 << 16) + 5381;
  593.                     #else
  594.                     int hash1 = 5381;
  595.                     #endif
  596.                     int hash2 = hash1;
  597.                    
  598.                     #if WIN32
  599.                     // 32bit machines.
  600.                     int* pint = (int*)src;
  601.                     int len = this.Length;
  602.                     while (len > 0) {
  603.                         hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
  604.                         if (len <= 2) {
  605.                             break;
  606.                         }
  607.                         hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
  608.                         pint += 2;
  609.                         len -= 4;
  610.                     }
  611.                     #else
  612.                     int c;
  613.                     char* s = src;
  614.                     while ((c = s[0]) != 0) {
  615.                         hash1 = ((hash1 << 5) + hash1) ^ c;
  616.                         c = s[1];
  617.                         if (c == 0)
  618.                             break;
  619.                         hash2 = ((hash2 << 5) + hash2) ^ c;
  620.                         s += 2;
  621.                     }
  622.                     #endif
  623.                     #if DEBUG
  624.                     // We want to ensure we can change our hash function daily.
  625.                     // This is perfectly fine as long as you don't persist the
  626.                     // value from GetHashCode to disk or count on String A
  627.                     // hashing before string B. Those are bugs in your code.
  628.                     hash1 ^= ThisAssembly.DailyBuildNumber;
  629.                     #endif
  630.                     return hash1 + (hash2 * 1566083941);
  631.                 }
  632.             }
  633.         }
  634.        
  635.         // Gets the length of this string
  636.         //
  637.         /// This is a EE implemented function so that the JIT can recognise is specially
  638.         /// and eliminate checks on character fetchs in a loop like:
  639.         /// for(int I = 0; I < str.Length; i++) str[i]
  640.         /// The actually code generated for this will be one instruction and will be inlined.
  641.         //
  642.         public extern int Length {
  643.             [MethodImplAttribute(MethodImplOptions.InternalCall)]
  644.             get;
  645.         }
  646.        
  647.         ///<internalonly/>
  648.         internal int ArrayLength {
  649.             get { return (m_arrayLength); }
  650.         }
  651.        
  652.         // Used by StringBuilder
  653.         internal int Capacity {
  654.             get { return (m_arrayLength - 1); }
  655.         }
  656.        
  657.         // Creates an array of strings by splitting this string at each
  658.         // occurence of a separator. The separator is searched for, and if found,
  659.         // the substring preceding the occurence is stored as the first element in
  660.         // the array of strings. We then continue in this manner by searching
  661.         // the substring that follows the occurence. On the other hand, if the separator
  662.         // is not found, the array of strings will contain this instance as its only element.
  663.         // If the separator is null
  664.         // whitespace (i.e., Character.IsWhitespace) is used as the separator.
  665.         //
  666.         public string[] Split(params char[] separator)
  667.         {
  668.             return Split(separator, Int32.MaxValue, StringSplitOptions.None);
  669.         }
  670.        
  671.         // Creates an array of strings by splitting this string at each
  672.         // occurence of a separator. The separator is searched for, and if found,
  673.         // the substring preceding the occurence is stored as the first element in
  674.         // the array of strings. We then continue in this manner by searching
  675.         // the substring that follows the occurence. On the other hand, if the separator
  676.         // is not found, the array of strings will contain this instance as its only element.
  677.         // If the spearator is the empty string (i.e., String.Empty), then
  678.         // whitespace (i.e., Character.IsWhitespace) is used as the separator.
  679.         // If there are more than count different strings, the last n-(count-1)
  680.         // elements are concatenated and added as the last String.
  681.         //
  682.         public string[] Split(char[] separator, int count)
  683.         {
  684.             return Split(separator, count, StringSplitOptions.None);
  685.         }
  686.        
  687.         [ComVisible(false)]
  688.         public string[] Split(char[] separator, StringSplitOptions options)
  689.         {
  690.             return Split(separator, Int32.MaxValue, options);
  691.         }
  692.        
  693.         [ComVisible(false)]
  694.         public string[] Split(char[] separator, int count, StringSplitOptions options)
  695.         {
  696.             if (count < 0) {
  697.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
  698.             }
  699.            
  700.             if (options < StringSplitOptions.None || options > StringSplitOptions.RemoveEmptyEntries) {
  701.                 throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options));
  702.             }
  703.            
  704.             bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);
  705.             if ((count == 0) || (omitEmptyEntries && this.Length == 0)) {
  706.                 return new string[0];
  707.             }
  708.            
  709.             int[] sepList = new int[Length];
  710.             int numReplaces = MakeSeparatorList(separator, ref sepList);
  711.            
  712.             //Handle the special case of no replaces and special count.
  713.             if (0 == numReplaces || count == 1) {
  714.                 string[] stringArray = new string[1];
  715.                 stringArray[0] = this;
  716.                 return stringArray;
  717.             }
  718.            
  719.             if (omitEmptyEntries) {
  720.                 return InternalSplitOmitEmptyEntries(sepList, null, numReplaces, count);
  721.             }
  722.             else {
  723.                 return InternalSplitKeepEmptyEntries(sepList, null, numReplaces, count);
  724.             }
  725.         }
  726.        
  727.         [ComVisible(false)]
  728.         public string[] Split(string[] separator, StringSplitOptions options)
  729.         {
  730.             return Split(separator, Int32.MaxValue, options);
  731.         }
  732.        
  733.         [ComVisible(false)]
  734.         public string[] Split(string[] separator, Int32 count, StringSplitOptions options)
  735.         {
  736.             if (count < 0) {
  737.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
  738.             }
  739.            
  740.             if (options < StringSplitOptions.None || options > StringSplitOptions.RemoveEmptyEntries) {
  741.                 throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options));
  742.             }
  743.            
  744.             bool omitEmptyEntries = (options == StringSplitOptions.RemoveEmptyEntries);
  745.            
  746.             if (separator == null || separator.Length == 0) {
  747.                 return Split((char[])null, count, options);
  748.             }
  749.            
  750.             if ((count == 0) || (omitEmptyEntries && this.Length == 0)) {
  751.                 return new string[0];
  752.             }
  753.            
  754.             int[] sepList = new int[Length];
  755.             int[] lengthList = new int[Length];
  756.             int numReplaces = MakeSeparatorList(separator, ref sepList, ref lengthList);
  757.            
  758.             //Handle the special case of no replaces and special count.
  759.             if (0 == numReplaces || count == 1) {
  760.                 string[] stringArray = new string[1];
  761.                 stringArray[0] = this;
  762.                 return stringArray;
  763.             }
  764.            
  765.             if (omitEmptyEntries) {
  766.                 return InternalSplitOmitEmptyEntries(sepList, lengthList, numReplaces, count);
  767.             }
  768.             else {
  769.                 return InternalSplitKeepEmptyEntries(sepList, lengthList, numReplaces, count);
  770.             }
  771.         }
  772.        
  773.         // Note a few special case in this function:
  774.         // If there is no separator in the string, a string array which only contains
  775.         // the original string will be returned regardless of the count.
  776.         //
  777.        
  778.         private string[] InternalSplitKeepEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 numReplaces, int count)
  779.         {
  780.             BCLDebug.Assert(count >= 2, "Count>=2");
  781.            
  782.             int currIndex = 0;
  783.             int arrIndex = 0;
  784.            
  785.             count--;
  786.             int numActualReplaces = (numReplaces < count) ? numReplaces : count;
  787.            
  788.             //Allocate space for the new array.
  789.             //+1 for the string from the end of the last replace to the end of the String.
  790.             string[] splitStrings = new string[numActualReplaces + 1];
  791.            
  792.             for (int i = 0; i < numActualReplaces && currIndex < Length; i++) {
  793.                 splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
  794.                 currIndex = sepList[i] + ((lengthList == null) ? 1 : lengthList[i]);
  795.             }
  796.            
  797.             //Handle the last string at the end of the array if there is one.
  798.             if (currIndex < Length && numActualReplaces >= 0) {
  799.                 splitStrings[arrIndex] = Substring(currIndex);
  800.             }
  801.             else if (arrIndex == numActualReplaces) {
  802.                 //We had a separator character at the end of a string. Rather than just allowing
  803.                 //a null character, we'll replace the last element in the array with an empty string.
  804.                 splitStrings[arrIndex] = String.Empty;
  805.                
  806.             }
  807.            
  808.             return splitStrings;
  809.         }
  810.        
  811.        
  812.         // This function will not keep the Empty String
  813.         private string[] InternalSplitOmitEmptyEntries(Int32[] sepList, Int32[] lengthList, Int32 numReplaces, int count)
  814.         {
  815.             BCLDebug.Assert(count >= 2, "Count>=2");
  816.            
  817.             // Allocate array to hold items. This array may not be
  818.             // filled completely in this function, we will create a
  819.             // new array and copy string references to that new array.
  820.            
  821.             int maxItems = (numReplaces < count) ? (numReplaces + 1) : count;
  822.             string[] splitStrings = new string[maxItems];
  823.            
  824.             int currIndex = 0;
  825.             int arrIndex = 0;
  826.            
  827.             for (int i = 0; i < numReplaces && currIndex < Length; i++) {
  828.                 if (sepList[i] - currIndex > 0) {
  829.                     splitStrings[arrIndex++] = Substring(currIndex, sepList[i] - currIndex);
  830.                 }
  831.                 currIndex = sepList[i] + ((lengthList == null) ? 1 : lengthList[i]);
  832.                 if (arrIndex == count - 1) {
  833.                     // If all the remaining entries at the end are empty, skip them
  834.                     while (i < numReplaces - 1 && currIndex == sepList[++i]) {
  835.                         currIndex += ((lengthList == null) ? 1 : lengthList[i]);
  836.                     }
  837.                     break;
  838.                 }
  839.             }
  840.            
  841.             // we must have at least one slot left to fill in the last string.
  842.             BCLDebug.Assert(arrIndex < maxItems, "arrIndex < maxItems");
  843.            
  844.             //Handle the last string at the end of the array if there is one.
  845.             if (currIndex < Length) {
  846.                 splitStrings[arrIndex++] = Substring(currIndex);
  847.             }
  848.            
  849.             string[] stringArray = splitStrings;
  850.             if (arrIndex != maxItems) {
  851.                 stringArray = new string[arrIndex];
  852.                 for (int j = 0; j < arrIndex; j++) {
  853.                     stringArray[j] = splitStrings[j];
  854.                 }
  855.             }
  856.             return stringArray;
  857.         }
  858.        
  859.         //--------------------------------------------------------------------
  860.         // This function returns number of the places within baseString where
  861.         // instances of characters in Separator occur.
  862.         // Args: separator -- A string containing all of the split characters.
  863.         // sepList -- an array of ints for split char indicies.
  864.         //--------------------------------------------------------------------
  865.         unsafe private int MakeSeparatorList(char[] separator, ref int[] sepList)
  866.         {
  867.             int foundCount = 0;
  868.            
  869.             if (separator == null || separator.Length == 0) {
  870.                 fixed (char* pwzChars = &this.m_firstChar) {
  871.                     //If they passed null or an empty string, look for whitespace.
  872.                     for (int i = 0; i < Length && foundCount < sepList.Length; i++) {
  873.                         if (Char.IsWhiteSpace(pwzChars[i])) {
  874.                             sepList[foundCount++] = i;
  875.                         }
  876.                     }
  877.                 }
  878.             }
  879.             else {
  880.                 int sepListCount = sepList.Length;
  881.                 int sepCount = separator.Length;
  882.                 //If they passed in a string of chars, actually look for those chars.
  883.                 fixed (char* pwzChars = &this.m_firstChar, pSepChars = separator) {
  884.                     for (int i = 0; i < Length && foundCount < sepListCount; i++) {
  885.                         char* pSep = pSepChars;
  886.                         for (int j = 0; j < sepCount; j++,pSep++) {
  887.                             if (pwzChars[i] == *pSep) {
  888.                                 sepList[foundCount++] = i;
  889.                                 break;
  890.                             }
  891.                         }
  892.                     }
  893.                 }
  894.             }
  895.             return foundCount;
  896.         }
  897.        
  898.         //--------------------------------------------------------------------
  899.         // This function returns number of the places within baseString where
  900.         // instances of separator strings occur.
  901.         // Args: separators -- An array containing all of the split strings.
  902.         // sepList -- an array of ints for split string indicies.
  903.         // lengthList -- an array of ints for split string lengths.
  904.         //--------------------------------------------------------------------
  905.         unsafe private int MakeSeparatorList(string[] separators, ref int[] sepList, ref int[] lengthList)
  906.         {
  907.             BCLDebug.Assert(separators != null && separators.Length > 0, "separators != null && separators.Length > 0");
  908.            
  909.             int foundCount = 0;
  910.             int sepListCount = sepList.Length;
  911.             int sepCount = separators.Length;
  912.            
  913.             fixed (char* pwzChars = &this.m_firstChar) {
  914.                 for (int i = 0; i < Length && foundCount < sepListCount; i++) {
  915.                     for (int j = 0; j < separators.Length; j++) {
  916.                         string separator = separators[j];
  917.                         if (String.IsNullOrEmpty(separator)) {
  918.                             continue;
  919.                         }
  920.                         Int32 currentSepLength = separator.Length;
  921.                         if (pwzChars[i] == separator[0] && currentSepLength <= Length - i) {
  922.                             if (currentSepLength == 1 || String.CompareOrdinal(this, i, separator, 0, currentSepLength) == 0) {
  923.                                 sepList[foundCount] = i;
  924.                                 lengthList[foundCount] = currentSepLength;
  925.                                 foundCount++;
  926.                                 i += currentSepLength - 1;
  927.                                 break;
  928.                             }
  929.                         }
  930.                     }
  931.                 }
  932.             }
  933.             return foundCount;
  934.         }
  935.        
  936.         // Returns a substring of this string.
  937.         //
  938.         public string Substring(int startIndex)
  939.         {
  940.             return this.Substring(startIndex, Length - startIndex);
  941.         }
  942.        
  943.         // Returns a substring of this string.
  944.         //
  945.         public string Substring(int startIndex, int length)
  946.         {
  947.             // okay to not enforce copying in the case of Substring(0, length), since we assume
  948.             // String instances are immutable.
  949.             return InternalSubStringWithChecks(startIndex, length, false);
  950.         }
  951.        
  952.        
  953.         internal string InternalSubStringWithChecks(int startIndex, int length, bool fAlwaysCopy)
  954.         {
  955.            
  956.             int thisLength = Length;
  957.            
  958.             //Bounds Checking.
  959.             if (startIndex < 0) {
  960.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  961.             }
  962.            
  963.             if (startIndex > thisLength) {
  964.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndexLargerThanLength"));
  965.             }
  966.            
  967.             if (length < 0) {
  968.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  969.             }
  970.            
  971.             if (startIndex > thisLength - length) {
  972.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_IndexLength"));
  973.             }
  974.            
  975.             if (length == 0) {
  976.                 return String.Empty;
  977.             }
  978.             return InternalSubString(startIndex, length, fAlwaysCopy);
  979.         }
  980.        
  981.         unsafe string InternalSubString(int startIndex, int length, bool fAlwaysCopy)
  982.         {
  983.             BCLDebug.Assert(startIndex >= 0 && startIndex <= this.Length, "StartIndex is out of range!");
  984.             BCLDebug.Assert(length >= 0 && startIndex <= this.Length - length, "length is out of range!");
  985.            
  986.             if (startIndex == 0 && length == this.Length && !fAlwaysCopy) {
  987.                 return this;
  988.             }
  989.            
  990.             string result = FastAllocateString(length);
  991.            
  992.             fixed (char* dest = &result.m_firstChar)
  993.                 fixed (char* src = &this.m_firstChar) {
  994.                     wstrcpy(dest, src + startIndex, length);
  995.                 }
  996.            
  997.             return result;
  998.         }
  999.        
  1000.         //This should really live on System.Globalization.CharacterInfo. However,
  1001.         //Trim gets called by security while resgen is running, so we can't run
  1002.         //CharacterInfo's class initializer (which goes to native and looks for a
  1003.         //resource table that hasn't yet been attached to the assembly when resgen
  1004.         //runs.
  1005.         static internal readonly char[] WhitespaceChars = {(char)9, (char)10, (char)11, (char)12, (char)13, (char)32, (char)133, (char)160, (char)5760, (char)8192,
  1006.         (char)8193, (char)8194, (char)8195, (char)8196, (char)8197, (char)8198, (char)8199, (char)8200, (char)8201, (char)8202,
  1007.         (char)8203, (char)8232, (char)8233, (char)12288, (char)65279};
  1008.        
  1009.         // Removes a string of characters from the ends of this string.
  1010.         public string Trim(params char[] trimChars)
  1011.         {
  1012.             if (null == trimChars || trimChars.Length == 0) {
  1013.                 trimChars = WhitespaceChars;
  1014.             }
  1015.             return TrimHelper(trimChars, TrimBoth);
  1016.         }
  1017.        
  1018.         // Removes a string of characters from the beginning of this string.
  1019.         public string TrimStart(params char[] trimChars)
  1020.         {
  1021.             if (null == trimChars || trimChars.Length == 0) {
  1022.                 trimChars = WhitespaceChars;
  1023.             }
  1024.             return TrimHelper(trimChars, TrimHead);
  1025.         }
  1026.        
  1027.        
  1028.         // Removes a string of characters from the end of this string.
  1029.         public string TrimEnd(params char[] trimChars)
  1030.         {
  1031.             if (null == trimChars || trimChars.Length == 0) {
  1032.                 trimChars = WhitespaceChars;
  1033.             }
  1034.             return TrimHelper(trimChars, TrimTail);
  1035.         }
  1036.        
  1037.        
  1038.         // Creates a new string with the characters copied in from ptr. If
  1039.         // ptr is null, a string initialized to ";<;No Object>;"; (i.e.,
  1040.         // String.NullString) is created.
  1041.         //
  1042.         [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
  1043.         unsafe public extern String(char* value);
  1044.         [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
  1045.         unsafe public extern String(char* value, int startIndex, int length);
  1046.        
  1047.         [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
  1048.         unsafe public extern String(sbyte* value);
  1049.         [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
  1050.         unsafe public extern String(sbyte* value, int startIndex, int length);
  1051.        
  1052.         [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
  1053.         unsafe public extern String(sbyte* value, int startIndex, int length, Encoding enc);
  1054.        
  1055.         unsafe private static string CreateString(sbyte* value, int startIndex, int length, Encoding enc)
  1056.         {
  1057.             if (enc == null)
  1058.                 return new string(value, startIndex, length);
  1059.             // default to ANSI
  1060.             if (length < 0)
  1061.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1062.             if (startIndex < 0) {
  1063.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  1064.             }
  1065.             if ((value + startIndex) < value) {
  1066.                 // overflow check
  1067.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
  1068.             }
  1069.             byte[] b = new byte[length];
  1070.            
  1071.             try {
  1072.                 Buffer.memcpy((byte*)value, startIndex, b, 0, length);
  1073.             }
  1074.             catch (NullReferenceException) {
  1075.                 // If we got a NullReferencException. It means the pointer or
  1076.                 // the index is out of range
  1077.                 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
  1078.             }
  1079.            
  1080.             return enc.GetString(b);
  1081.         }
  1082.        
  1083.         // Helper for encodings so they can talk to our buffer directly
  1084.         // stringLength must be the exact size we'll expect
  1085.         unsafe static internal string CreateStringFromEncoding(byte* bytes, int byteLength, Encoding encoding)
  1086.         {
  1087.             BCLDebug.Assert(bytes != null, "need a byte[].");
  1088.             BCLDebug.Assert(byteLength >= 0, "byteLength >= 0");
  1089.            
  1090.             // Get our string length
  1091.             int stringLength = encoding.GetCharCount(bytes, byteLength, null);
  1092.             BCLDebug.Assert(stringLength >= 0, "stringLength >= 0");
  1093.            
  1094.             // They gave us an empty string if they needed one
  1095.             // 0 bytelength might be possible if there's something in an encoder
  1096.             if (stringLength == 0)
  1097.                 return String.Empty;
  1098.            
  1099.             string s = FastAllocateString(stringLength);
  1100.             fixed (char* pTempChars = &s.m_firstChar) {
  1101.                 int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
  1102.                 BCLDebug.Assert(stringLength == doubleCheck, "Expected encoding.GetChars to return same length as encoding.GetCharCount");
  1103.             }
  1104.            
  1105.             return s;
  1106.         }
  1107.        
  1108.         unsafe internal byte[] ConvertToAnsi_BestFit_Throw(int iMaxDBCSCharByteSize)
  1109.         {
  1110.             const uint CP_ACP = 0;
  1111.             int nb;
  1112.            
  1113.             int cbNativeBuffer = (Length + 3) * iMaxDBCSCharByteSize;
  1114.             byte[] bytes = new byte[cbNativeBuffer];
  1115.            
  1116.             uint flgs = 0;
  1117.             uint DefaultCharUsed = 0;
  1118.            
  1119.             fixed (byte* pbNativeBuffer = bytes) {
  1120.                 fixed (char* pwzChar = &this.m_firstChar) {
  1121.                     nb = Win32Native.WideCharToMultiByte(CP_ACP, flgs, pwzChar, this.Length, pbNativeBuffer, cbNativeBuffer, IntPtr.Zero, new IntPtr(&DefaultCharUsed));
  1122.                 }
  1123.             }
  1124.            
  1125.             if (0 != DefaultCharUsed) {
  1126.                 throw new ArgumentException(Environment.GetResourceString("Interop_Marshal_Unmappable_Char"));
  1127.             }
  1128.            
  1129.             bytes[nb] = 0;
  1130.             return bytes;
  1131.         }
  1132.        
  1133.         // Normalization Methods
  1134.         // These just wrap calls to Normalization class
  1135.         public bool IsNormalized()
  1136.         {
  1137.             // Default to Form C
  1138.             return IsNormalized(NormalizationForm.FormC);
  1139.         }
  1140.        
  1141.         public bool IsNormalized(NormalizationForm normalizationForm)
  1142.         {
  1143.             if (this.IsFastSort()) {
  1144.                 // If its FastSort && one of the 4 main forms, then its already normalized
  1145.                 if (normalizationForm == NormalizationForm.FormC || normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormD || normalizationForm == NormalizationForm.FormKD)
  1146.                     return true;
  1147.             }
  1148.             return Normalization.IsNormalized(this, normalizationForm);
  1149.         }
  1150.        
  1151.         public string Normalize()
  1152.         {
  1153.             // Default to Form C
  1154.             return Normalize(NormalizationForm.FormC);
  1155.         }
  1156.        
  1157.         public string Normalize(NormalizationForm normalizationForm)
  1158.         {
  1159.             if (this.IsAscii()) {
  1160.                 // If its FastSort && one of the 4 main forms, then its already normalized
  1161.                 if (normalizationForm == NormalizationForm.FormC || normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormD || normalizationForm == NormalizationForm.FormKD)
  1162.                     return this;
  1163.             }
  1164.             return Normalization.Normalize(this, normalizationForm);
  1165.         }
  1166.        
  1167.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1168.         private static extern string FastAllocateString(int length);
  1169.        
  1170.         unsafe private static void FillStringChecked(string dest, int destPos, string src)
  1171.         {
  1172.             int length = src.Length;
  1173.            
  1174.             if (length > dest.Length - destPos) {
  1175.                 throw new IndexOutOfRangeException();
  1176.             }
  1177.            
  1178.             fixed (char* pDest = &dest.m_firstChar)
  1179.                 fixed (char* pSrc = &src.m_firstChar) {
  1180.                     wstrcpy(pDest + destPos, pSrc, length);
  1181.                 }
  1182.         }
  1183.        
  1184.         // Creates a new string from the characters in a subarray. The new string will
  1185.         // be created from the characters in value between startIndex and
  1186.         // startIndex + length - 1.
  1187.         //
  1188.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1189.         public extern String(char[] value, int startIndex, int length);
  1190.        
  1191.         // Creates a new string from the characters in a subarray. The new string will be
  1192.         // created from the characters in value.
  1193.         //
  1194.        
  1195.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1196.         public extern String(char[] value);
  1197.        
  1198.         //
  1199.         // This handles the case where both smem and dmem pointers are
  1200.         // aligned on a pointer boundary
  1201.         //
  1202.         unsafe private static void wstrcpyPtrAligned(char* dmem, char* smem, int charCount)
  1203.         {
  1204.             #if _DEBUG
  1205.             BCLDebug.Assert(((int)dmem & (IntPtr.Size - 1)) == 0, "dmem is pointer size aligned");
  1206.             BCLDebug.Assert(((int)smem & (IntPtr.Size - 1)) == 0, "smem is pointer size aligned");
  1207.             #endif
  1208.            
  1209.             while (charCount >= 8) {
  1210.                 ((uint*)dmem)[0] = ((uint*)smem)[0];
  1211.                 ((uint*)dmem)[1] = ((uint*)smem)[1];
  1212.                 ((uint*)dmem)[2] = ((uint*)smem)[2];
  1213.                 ((uint*)dmem)[3] = ((uint*)smem)[3];
  1214.                
  1215.                 dmem += 8;
  1216.                 smem += 8;
  1217.                 charCount -= 8;
  1218.             }
  1219.             if ((charCount & 4) != 0) {
  1220.                 ((uint*)dmem)[0] = ((uint*)smem)[0];
  1221.                 ((uint*)dmem)[1] = ((uint*)smem)[1];
  1222.                 dmem += 4;
  1223.                 smem += 4;
  1224.             }
  1225.             if ((charCount & 2) != 0) {
  1226.                 ((uint*)dmem)[0] = ((uint*)smem)[0];
  1227.                 dmem += 2;
  1228.                 smem += 2;
  1229.             }
  1230.            
  1231.             if ((charCount & 1) != 0) {
  1232.                 dmem[0] = smem[0];
  1233.             }
  1234.         }
  1235.        
  1236.         unsafe private static void wstrcpy(char* dmem, char* smem, int charCount)
  1237.         {
  1238.             if (charCount > 0) {
  1239.                 #if ALIGN_ACCESS
  1240.                 if ((((int)dmem | (int)smem) & 1) == 0) {
  1241.                     #endif
  1242.                     // First Align dmem to a pointer boundary
  1243.                     if (((int)dmem & 2) != 0) {
  1244.                         dmem[0] = smem[0];
  1245.                         dmem += 1;
  1246.                         smem += 1;
  1247.                         charCount -= 1;
  1248.                     }
  1249.                     // Both x86 and AMD64 perform much faster if all writes are pointer aligned
  1250.                     // Unaligned reads perform better than 2-byte aligned reads and
  1251.                     // better than pointer aligned reads with 16-bit shift and OR operation
  1252.                     // So on x86 or AMD64 after aligning dmem to a pointer boundry
  1253.                     // we just use standard mechanism
  1254.                     while (charCount >= 8) {
  1255.                         ((uint*)dmem)[0] = ((uint*)smem)[0];
  1256.                         ((uint*)dmem)[1] = ((uint*)smem)[1];
  1257.                         ((uint*)dmem)[2] = ((uint*)smem)[2];
  1258.                         ((uint*)dmem)[3] = ((uint*)smem)[3];
  1259.                         dmem += 8;
  1260.                         smem += 8;
  1261.                         charCount -= 8;
  1262.                     }
  1263.                     if ((charCount & 4) != 0) {
  1264.                         ((uint*)dmem)[0] = ((uint*)smem)[0];
  1265.                         ((uint*)dmem)[1] = ((uint*)smem)[1];
  1266.                         dmem += 4;
  1267.                         smem += 4;
  1268.                     }
  1269.                     if ((charCount & 2) != 0) {
  1270.                         ((uint*)dmem)[0] = ((uint*)smem)[0];
  1271.                         dmem += 2;
  1272.                         smem += 2;
  1273.                     }
  1274.                     if ((charCount & 1) != 0) {
  1275.                         dmem[0] = smem[0];
  1276.                     }
  1277.                     #if ALIGN_ACCESS
  1278.                 }
  1279.                 else {
  1280.                     // This is rare case where at least one of the pointers is only byte aligned.
  1281.                     do {
  1282.                         ((byte*)dmem)[0] = ((byte*)smem)[0];
  1283.                         ((byte*)dmem)[1] = ((byte*)smem)[1];
  1284.                         charCount -= 1;
  1285.                         dmem += 1;
  1286.                         smem += 1;
  1287.                     }
  1288.                     while (charCount > 0);
  1289.                 }
  1290.                 #endif
  1291.             }
  1292.         }
  1293.        
  1294.         private string CtorCharArray(char[] value)
  1295.         {
  1296.             if (value != null && value.Length != 0) {
  1297.                 string result = FastAllocateString(value.Length);
  1298.                
  1299.                 unsafe {
  1300.                     fixed (char* dest = result, source = value) {
  1301.                         wstrcpyPtrAligned(dest, source, value.Length);
  1302.                     }
  1303.                 }
  1304.                 return result;
  1305.             }
  1306.             else
  1307.                 return String.Empty;
  1308.         }
  1309.        
  1310.         private string CtorCharArrayStartLength(char[] value, int startIndex, int length)
  1311.         {
  1312.             if (value == null)
  1313.                 throw new ArgumentNullException("value");
  1314.            
  1315.             if (startIndex < 0)
  1316.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  1317.            
  1318.             if (length < 0)
  1319.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  1320.            
  1321.             if (startIndex > value.Length - length)
  1322.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1323.            
  1324.             if (length > 0) {
  1325.                 string result = FastAllocateString(length);
  1326.                
  1327.                 unsafe {
  1328.                     fixed (char* dest = result, source = value) {
  1329.                         wstrcpy(dest, source + startIndex, length);
  1330.                     }
  1331.                 }
  1332.                 return result;
  1333.             }
  1334.             else
  1335.                 return String.Empty;
  1336.         }
  1337.        
  1338.         private string CtorCharCount(char c, int count)
  1339.         {
  1340.             if (count > 0) {
  1341.                 string result = FastAllocateString(count);
  1342.                 unsafe {
  1343.                     fixed (char* dest = result) {
  1344.                         char* dmem = dest;
  1345.                         while (((uint)dmem & 3) != 0 && count > 0) {
  1346.                             *dmem++ = c;
  1347.                             count--;
  1348.                         }
  1349.                         uint cc = (uint)((c << 16) | c);
  1350.                         if (count >= 4) {
  1351.                             count -= 4;
  1352.                             do {
  1353.                                 ((uint*)dmem)[0] = cc;
  1354.                                 ((uint*)dmem)[1] = cc;
  1355.                                 dmem += 4;
  1356.                                 count -= 4;
  1357.                             }
  1358.                             while (count >= 0);
  1359.                         }
  1360.                         if ((count & 2) != 0) {
  1361.                             ((uint*)dmem)[0] = cc;
  1362.                             dmem += 2;
  1363.                         }
  1364.                         if ((count & 1) != 0)
  1365.                             dmem[0] = c;
  1366.                     }
  1367.                 }
  1368.                 return result;
  1369.             }
  1370.             else if (count == 0)
  1371.                 return String.Empty;
  1372.             else
  1373.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", "count"));
  1374.         }
  1375.        
  1376.         unsafe private static int wcslen(char* ptr)
  1377.         {
  1378.             char* end = ptr;
  1379.            
  1380.             // The following code is (somewhat surprisingly!) significantly faster than a naive loop,
  1381.             // at least on x86 and the current jit.
  1382.            
  1383.             // First make sure our pointer is aligned on a dword boundary
  1384.             while (((uint)end & 3) != 0 && *end != 0)
  1385.                 end++;
  1386.             if (*end != 0) {
  1387.                 // The loop condition below works because if "end[0] & end[1]" is non-zero, that means
  1388.                 // neither operand can have been zero. If is zero, we have to look at the operands individually,
  1389.                 // but we hope this going to fairly rare.
  1390.                
  1391.                 // In general, it would be incorrect to access end[1] if we haven't made sure
  1392.                 // end[0] is non-zero. However, we know the ptr has been aligned by the loop above
  1393.                 // so end[0] and end[1] must be in the same page, so they're either both accessible, or both not.
  1394.                
  1395.                 while ((end[0] & end[1]) != 0 || (end[0] != 0 && end[1] != 0)) {
  1396.                     end += 2;
  1397.                 }
  1398.             }
  1399.             // finish up with the naive loop
  1400.             for (; *end != 0; end++)
  1401.                 ;
  1402.            
  1403.             int count = (int)(end - ptr);
  1404.            
  1405.             return count;
  1406.         }
  1407.        
  1408.         unsafe private string CtorCharPtr(char* ptr)
  1409.         {
  1410.             BCLDebug.Assert(this == null, "this == null");
  1411.             // this is the string constructor, we allocate it
  1412.             if (ptr >= (char*)64000) {
  1413.                 try {
  1414.                     int count = wcslen(ptr);
  1415.                     string result = FastAllocateString(count);
  1416.                     fixed (char* dest = result)
  1417.                         wstrcpy(dest, ptr, count);
  1418.                     return result;
  1419.                 }
  1420.                 catch (NullReferenceException) {
  1421.                     throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
  1422.                 }
  1423.             }
  1424.             else if (ptr == null)
  1425.                 return String.Empty;
  1426.             else
  1427.                 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeStringPtrNotAtom"));
  1428.         }
  1429.        
  1430.         unsafe private string CtorCharPtrStartLength(char* ptr, int startIndex, int length)
  1431.         {
  1432.             BCLDebug.Assert(this == null, "this == null");
  1433.             // this is the string constructor, we allocate it
  1434.             if (length < 0) {
  1435.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  1436.             }
  1437.            
  1438.             if (startIndex < 0) {
  1439.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  1440.             }
  1441.            
  1442.             char* pFrom = ptr + startIndex;
  1443.             if (pFrom < ptr) {
  1444.                 // This means that the pointer operation has had an overflow
  1445.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
  1446.             }
  1447.            
  1448.             string result = FastAllocateString(length);
  1449.            
  1450.             try {
  1451.                 fixed (char* dest = result)
  1452.                     wstrcpy(dest, pFrom, length);
  1453.                 return result;
  1454.             }
  1455.             catch (NullReferenceException) {
  1456.                 throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
  1457.             }
  1458.         }
  1459.        
  1460.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1461.         public extern String(char c, int count);
  1462.        
  1463.         //
  1464.         //
  1465.         // INSTANCE METHODS
  1466.         //
  1467.         //
  1468.        
  1469.         // Provides a culture-correct string comparison. StrA is compared to StrB
  1470.         // to determine whether it is lexicographically less, equal, or greater, and then returns
  1471.         // either a negative integer, 0, or a positive integer; respectively.
  1472.         //
  1473.         public static int Compare(string strA, string strB)
  1474.         {
  1475.             return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
  1476.         }
  1477.        
  1478.         // Provides a culture-correct string comparison. strA is compared to strB
  1479.         // to determine whether it is lexicographically less, equal, or greater, and then a
  1480.         // negative integer, 0, or a positive integer is returned; respectively.
  1481.         // The case-sensitive option is set by ignoreCase
  1482.         //
  1483.         public static int Compare(string strA, string strB, bool ignoreCase)
  1484.         {
  1485.             if (ignoreCase) {
  1486.                 return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
  1487.             }
  1488.             return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
  1489.         }
  1490.        
  1491.         // Provides a more flexible function for string comparision. See StringComparison
  1492.         // for meaning of different comparisonType.
  1493.         public static int Compare(string strA, string strB, StringComparison comparisonType)
  1494.         {
  1495.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  1496.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  1497.             }
  1498.            
  1499.             if ((object)strA == (object)strB) {
  1500.                 return 0;
  1501.             }
  1502.            
  1503.             //they can't both be null;
  1504.             if (strA == null) {
  1505.                 return -1;
  1506.             }
  1507.            
  1508.             if (strB == null) {
  1509.                 return 1;
  1510.             }
  1511.            
  1512.            
  1513.             switch (comparisonType) {
  1514.                 case StringComparison.CurrentCulture:
  1515.                     return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
  1516.                 case StringComparison.CurrentCultureIgnoreCase:
  1517.                    
  1518.                     return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
  1519.                 case StringComparison.InvariantCulture:
  1520.                    
  1521.                     return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
  1522.                 case StringComparison.InvariantCultureIgnoreCase:
  1523.                    
  1524.                     return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
  1525.                 case StringComparison.Ordinal:
  1526.                    
  1527.                     return CompareOrdinalHelper(strA, strB);
  1528.                 case StringComparison.OrdinalIgnoreCase:
  1529.                    
  1530.                     // If both strings are ASCII strings, we can take the fast path.
  1531.                     if (strA.IsAscii() && strB.IsAscii()) {
  1532.                         return (String.nativeCompareOrdinal(strA, strB, true));
  1533.                     }
  1534.                     // Take the slow path.
  1535.                     return TextInfo.CompareOrdinalIgnoreCase(strA, strB);
  1536.                 default:
  1537.                    
  1538.                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_StringComparison"));
  1539.                     break;
  1540.             }
  1541.         }
  1542.        
  1543.         // Provides a culture-correct string comparison. strA is compared to strB
  1544.         // to determine whether it is lexicographically less, equal, or greater, and then a
  1545.         // negative integer, 0, or a positive integer is returned; respectively.
  1546.         // The case-sensitive option is set by ignoreCase, and the culture is set
  1547.         // by culture
  1548.         //
  1549.         public static int Compare(string strA, string strB, bool ignoreCase, CultureInfo culture)
  1550.         {
  1551.             if (culture == null) {
  1552.                 throw new ArgumentNullException("culture");
  1553.             }
  1554.            
  1555.             if (ignoreCase) {
  1556.                 return culture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase);
  1557.             }
  1558.             return culture.CompareInfo.Compare(strA, strB, CompareOptions.None);
  1559.         }
  1560.        
  1561.         // Determines whether two string regions match. The substring of strA beginning
  1562.         // at indexA of length count is compared with the substring of strB
  1563.         // beginning at indexB of the same length.
  1564.         //
  1565.         public static int Compare(string strA, int indexA, string strB, int indexB, int length)
  1566.         {
  1567.             int lengthA = length;
  1568.             int lengthB = length;
  1569.            
  1570.             if (strA != null) {
  1571.                 if (strA.Length - indexA < lengthA) {
  1572.                     lengthA = (strA.Length - indexA);
  1573.                 }
  1574.             }
  1575.            
  1576.             if (strB != null) {
  1577.                 if (strB.Length - indexB < lengthB) {
  1578.                     lengthB = (strB.Length - indexB);
  1579.                 }
  1580.             }
  1581.             return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
  1582.         }
  1583.        
  1584.        
  1585.         // Determines whether two string regions match. The substring of strA beginning
  1586.         // at indexA of length count is compared with the substring of strB
  1587.         // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean.
  1588.         //
  1589.         public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase)
  1590.         {
  1591.             int lengthA = length;
  1592.             int lengthB = length;
  1593.            
  1594.             if (strA != null) {
  1595.                 if (strA.Length - indexA < lengthA) {
  1596.                     lengthA = (strA.Length - indexA);
  1597.                 }
  1598.             }
  1599.            
  1600.             if (strB != null) {
  1601.                 if (strB.Length - indexB < lengthB) {
  1602.                     lengthB = (strB.Length - indexB);
  1603.                 }
  1604.             }
  1605.            
  1606.             if (ignoreCase) {
  1607.                 return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
  1608.             }
  1609.             return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
  1610.         }
  1611.        
  1612.         // Determines whether two string regions match. The substring of strA beginning
  1613.         // at indexA of length length is compared with the substring of strB
  1614.         // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean,
  1615.         // and the culture is set by culture.
  1616.         //
  1617.         public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
  1618.         {
  1619.             if (culture == null) {
  1620.                 throw new ArgumentNullException("culture");
  1621.             }
  1622.            
  1623.             int lengthA = length;
  1624.             int lengthB = length;
  1625.            
  1626.             if (strA != null) {
  1627.                 if (strA.Length - indexA < lengthA) {
  1628.                     lengthA = (strA.Length - indexA);
  1629.                 }
  1630.             }
  1631.            
  1632.             if (strB != null) {
  1633.                 if (strB.Length - indexB < lengthB) {
  1634.                     lengthB = (strB.Length - indexB);
  1635.                 }
  1636.             }
  1637.            
  1638.             if (ignoreCase) {
  1639.                 return culture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
  1640.             }
  1641.             else {
  1642.                 return culture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
  1643.             }
  1644.         }
  1645.        
  1646.         public static int Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
  1647.         {
  1648.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  1649.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  1650.             }
  1651.            
  1652.             if (strA == null || strB == null) {
  1653.                 if ((object)strA == (object)strB) {
  1654.                     //they're both null;
  1655.                     return 0;
  1656.                 }
  1657.                
  1658.                 return (strA == null) ? -1 : 1;
  1659.                 //-1 if A is null, 1 if B is null.
  1660.             }
  1661.            
  1662.             if (length < 0) {
  1663.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  1664.             }
  1665.            
  1666.             if (indexA < 0) {
  1667.                 throw new ArgumentOutOfRangeException("indexA", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1668.             }
  1669.            
  1670.             if (indexB < 0) {
  1671.                 throw new ArgumentOutOfRangeException("indexB", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1672.             }
  1673.            
  1674.             if (strA.Length - indexA < 0) {
  1675.                 throw new ArgumentOutOfRangeException("indexA", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1676.             }
  1677.            
  1678.             if (strB.Length - indexB < 0) {
  1679.                 throw new ArgumentOutOfRangeException("indexB", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1680.             }
  1681.            
  1682.             if ((length == 0) || ((strA == strB) && (indexA == indexB))) {
  1683.                 return 0;
  1684.             }
  1685.            
  1686.             int lengthA = length;
  1687.             int lengthB = length;
  1688.            
  1689.             if (strA != null) {
  1690.                 if (strA.Length - indexA < lengthA) {
  1691.                     lengthA = (strA.Length - indexA);
  1692.                 }
  1693.             }
  1694.            
  1695.             if (strB != null) {
  1696.                 if (strB.Length - indexB < lengthB) {
  1697.                     lengthB = (strB.Length - indexB);
  1698.                 }
  1699.             }
  1700.            
  1701.             switch (comparisonType) {
  1702.                 case StringComparison.CurrentCulture:
  1703.                     return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
  1704.                 case StringComparison.CurrentCultureIgnoreCase:
  1705.                    
  1706.                     return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
  1707.                 case StringComparison.InvariantCulture:
  1708.                    
  1709.                     return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None);
  1710.                 case StringComparison.InvariantCultureIgnoreCase:
  1711.                    
  1712.                     return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase);
  1713.                 case StringComparison.Ordinal:
  1714.                    
  1715.                     return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length);
  1716.                 case StringComparison.OrdinalIgnoreCase:
  1717.                    
  1718.                     return (TextInfo.CompareOrdinalIgnoreCaseEx(strA, indexA, strB, indexB, length));
  1719.                 default:
  1720.                    
  1721.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"));
  1722.                     break;
  1723.             }
  1724.            
  1725.         }
  1726.        
  1727.         // Compares this object to another object, returning an integer that
  1728.         // indicates the relationship. This method returns a value less than 0 if this is less than value, 0
  1729.         // if this is equal to value, or a value greater than 0
  1730.         // if this is greater than value. Strings are considered to be
  1731.         // greater than all non-String objects. Note that this means sorted
  1732.         // arrays would contain nulls, other objects, then Strings in that order.
  1733.         //
  1734.         public int CompareTo(object value)
  1735.         {
  1736.             if (value == null) {
  1737.                 return 1;
  1738.             }
  1739.            
  1740.             if (!(value is string)) {
  1741.                 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeString"));
  1742.             }
  1743.            
  1744.             return String.Compare(this, (string)value, StringComparison.CurrentCulture);
  1745.         }
  1746.        
  1747.         // Determines the sorting relation of StrB to the current instance.
  1748.         //
  1749.         public int CompareTo(string strB)
  1750.         {
  1751.             if (strB == null) {
  1752.                 return 1;
  1753.             }
  1754.             return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, 0);
  1755.         }
  1756.        
  1757.         // Compares strA and strB using an ordinal (code-point) comparison.
  1758.         //
  1759.         public static int CompareOrdinal(string strA, string strB)
  1760.         {
  1761.             if ((object)strA == (object)strB) {
  1762.                 return 0;
  1763.             }
  1764.            
  1765.             //they can't both be null;
  1766.             if (strA == null) {
  1767.                 return -1;
  1768.             }
  1769.            
  1770.             if (strB == null) {
  1771.                 return 1;
  1772.             }
  1773.            
  1774.             return CompareOrdinalHelper(strA, strB);
  1775.         }
  1776.        
  1777.        
  1778.         // Compares strA and strB using an ordinal (code-point) comparison.
  1779.         //
  1780.         public static int CompareOrdinal(string strA, int indexA, string strB, int indexB, int length)
  1781.         {
  1782.             if (strA == null || strB == null) {
  1783.                 if ((object)strA == (object)strB) {
  1784.                     //they're both null;
  1785.                     return 0;
  1786.                 }
  1787.                
  1788.                 return (strA == null) ? -1 : 1;
  1789.                 //-1 if A is null, 1 if B is null.
  1790.             }
  1791.            
  1792.             return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length);
  1793.         }
  1794.        
  1795.         public bool Contains(string value)
  1796.         {
  1797.             return (IndexOf(value, StringComparison.Ordinal) >= 0);
  1798.         }
  1799.        
  1800.        
  1801.         // Determines whether a specified string is a suffix of the the current instance.
  1802.         //
  1803.         // The case-sensitive and culture-sensitive option is set by options,
  1804.         // and the default culture is used.
  1805.         //
  1806.         public bool EndsWith(string value)
  1807.         {
  1808.             return EndsWith(value, false, null);
  1809.         }
  1810.        
  1811.         [ComVisible(false)]
  1812.         public bool EndsWith(string value, StringComparison comparisonType)
  1813.         {
  1814.             if ((object)value == null) {
  1815.                 throw new ArgumentNullException("value");
  1816.             }
  1817.            
  1818.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  1819.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  1820.             }
  1821.            
  1822.             if ((object)this == (object)value) {
  1823.                 return true;
  1824.             }
  1825.            
  1826.             if (value.Length == 0) {
  1827.                 return true;
  1828.             }
  1829.            
  1830.             switch (comparisonType) {
  1831.                 case StringComparison.CurrentCulture:
  1832.                     return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(this, value, CompareOptions.None);
  1833.                 case StringComparison.CurrentCultureIgnoreCase:
  1834.                    
  1835.                     return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(this, value, CompareOptions.IgnoreCase);
  1836.                 case StringComparison.InvariantCulture:
  1837.                    
  1838.                     return CultureInfo.InvariantCulture.CompareInfo.IsSuffix(this, value, CompareOptions.None);
  1839.                 case StringComparison.InvariantCultureIgnoreCase:
  1840.                    
  1841.                     return CultureInfo.InvariantCulture.CompareInfo.IsSuffix(this, value, CompareOptions.IgnoreCase);
  1842.                 case StringComparison.Ordinal:
  1843.                    
  1844.                     return this.Length < value.Length ? false : (nativeCompareOrdinalEx(this, this.Length - value.Length, value, 0, value.Length) == 0);
  1845.                 case StringComparison.OrdinalIgnoreCase:
  1846.                    
  1847.                     return this.Length < value.Length ? false : (TextInfo.CompareOrdinalIgnoreCaseEx(this, this.Length - value.Length, value, 0, value.Length) == 0);
  1848.                 default:
  1849.                    
  1850.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  1851.                     break;
  1852.             }
  1853.         }
  1854.        
  1855.         public bool EndsWith(string value, bool ignoreCase, CultureInfo culture)
  1856.         {
  1857.             if (null == value) {
  1858.                 throw new ArgumentNullException("value");
  1859.             }
  1860.            
  1861.             if ((object)this == (object)value) {
  1862.                 return true;
  1863.             }
  1864.            
  1865.             CultureInfo referenceCulture = (culture == null) ? CultureInfo.CurrentCulture : culture;
  1866.             return referenceCulture.CompareInfo.IsSuffix(this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  1867.         }
  1868.        
  1869.         internal bool EndsWith(char value)
  1870.         {
  1871.             int thisLen = this.Length;
  1872.             if (thisLen != 0) {
  1873.                 if (this[thisLen - 1] == value)
  1874.                     return true;
  1875.             }
  1876.             return false;
  1877.         }
  1878.        
  1879.        
  1880.         // Returns the index of the first occurance of value in the current instance.
  1881.         // The search starts at startIndex and runs thorough the next count characters.
  1882.         //
  1883.         public int IndexOf(char value)
  1884.         {
  1885.             return IndexOf(value, 0, this.Length);
  1886.         }
  1887.        
  1888.         public int IndexOf(char value, int startIndex)
  1889.         {
  1890.             return IndexOf(value, startIndex, this.Length - startIndex);
  1891.         }
  1892.        
  1893.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1894.         public extern int IndexOf(char value, int startIndex, int count);
  1895.        
  1896.         // Returns the index of the first occurance of any character in value in the current instance.
  1897.         // The search starts at startIndex and runs to endIndex-1. [startIndex,endIndex).
  1898.         //
  1899.        
  1900.         public int IndexOfAny(char[] anyOf)
  1901.         {
  1902.             return IndexOfAny(anyOf, 0, this.Length);
  1903.         }
  1904.        
  1905.         public int IndexOfAny(char[] anyOf, int startIndex)
  1906.         {
  1907.             return IndexOfAny(anyOf, startIndex, this.Length - startIndex);
  1908.         }
  1909.        
  1910.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1911.         public extern int IndexOfAny(char[] anyOf, int startIndex, int count);
  1912.        
  1913.        
  1914.         // Determines the position within this string of the first occurence of the specified
  1915.         // string, according to the specified search criteria. The search begins at
  1916.         // the first character of this string, it is case-sensitive and culture-sensitive,
  1917.         // and the default culture is used.
  1918.         //
  1919.         public int IndexOf(string value)
  1920.         {
  1921.             return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value);
  1922.         }
  1923.        
  1924.         // Determines the position within this string of the first occurence of the specified
  1925.         // string, according to the specified search criteria. The search begins at
  1926.         // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used.
  1927.         //
  1928.         public int IndexOf(string value, int startIndex)
  1929.         {
  1930.             return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex);
  1931.         }
  1932.        
  1933.         // Determines the position within this string of the first occurence of the specified
  1934.         // string, according to the specified search criteria. The search begins at
  1935.         // startIndex, ends at endIndex and the default culture is used.
  1936.         //
  1937.         public int IndexOf(string value, int startIndex, int count)
  1938.         {
  1939.             if (startIndex < 0 || startIndex > this.Length) {
  1940.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1941.             }
  1942.            
  1943.             if (count < 0 || count > this.Length - startIndex) {
  1944.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
  1945.             }
  1946.            
  1947.             return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
  1948.         }
  1949.        
  1950.         public int IndexOf(string value, StringComparison comparisonType)
  1951.         {
  1952.             return IndexOf(value, 0, this.Length, comparisonType);
  1953.         }
  1954.        
  1955.         public int IndexOf(string value, int startIndex, StringComparison comparisonType)
  1956.         {
  1957.             return IndexOf(value, startIndex, this.Length - startIndex, comparisonType);
  1958.         }
  1959.        
  1960.         public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType)
  1961.         {
  1962.             // Validate inputs
  1963.             if (value == null)
  1964.                 throw new ArgumentNullException("value");
  1965.            
  1966.             if (startIndex < 0 || startIndex > this.Length)
  1967.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1968.            
  1969.             if (count < 0 || startIndex > this.Length - count)
  1970.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
  1971.            
  1972.            
  1973.             switch (comparisonType) {
  1974.                 case StringComparison.CurrentCulture:
  1975.                     return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
  1976.                 case StringComparison.CurrentCultureIgnoreCase:
  1977.                    
  1978.                     return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
  1979.                 case StringComparison.InvariantCulture:
  1980.                    
  1981.                     return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
  1982.                 case StringComparison.InvariantCultureIgnoreCase:
  1983.                    
  1984.                     return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
  1985.                 case StringComparison.Ordinal:
  1986.                    
  1987.                     return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.Ordinal);
  1988.                 case StringComparison.OrdinalIgnoreCase:
  1989.                    
  1990.                     return TextInfo.IndexOfStringOrdinalIgnoreCase(this, value, startIndex, count);
  1991.                 default:
  1992.                    
  1993.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  1994.                     break;
  1995.             }
  1996.         }
  1997.        
  1998.         // Returns the index of the last occurance of value in the current instance.
  1999.         // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
  2000.         // The character at position startIndex is included in the search. startIndex is the larger
  2001.         // index within the string.
  2002.         //
  2003.         public int LastIndexOf(char value)
  2004.         {
  2005.             return LastIndexOf(value, this.Length - 1, this.Length);
  2006.         }
  2007.        
  2008.         public int LastIndexOf(char value, int startIndex)
  2009.         {
  2010.             return LastIndexOf(value, startIndex, startIndex + 1);
  2011.         }
  2012.        
  2013.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2014.         public extern int LastIndexOf(char value, int startIndex, int count);
  2015.        
  2016.         // Returns the index of the last occurance of any character in value in the current instance.
  2017.         // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
  2018.         // The character at position startIndex is included in the search. startIndex is the larger
  2019.         // index within the string.
  2020.         //
  2021.        
  2022.         public int LastIndexOfAny(char[] anyOf)
  2023.         {
  2024.             return LastIndexOfAny(anyOf, this.Length - 1, this.Length);
  2025.         }
  2026.        
  2027.         public int LastIndexOfAny(char[] anyOf, int startIndex)
  2028.         {
  2029.             return LastIndexOfAny(anyOf, startIndex, startIndex + 1);
  2030.         }
  2031.        
  2032.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2033.         public extern int LastIndexOfAny(char[] anyOf, int startIndex, int count);
  2034.        
  2035.        
  2036.         // Returns the index of the last occurance of any character in value in the current instance.
  2037.         // The search starts at startIndex and runs to endIndex. [startIndex,endIndex].
  2038.         // The character at position startIndex is included in the search. startIndex is the larger
  2039.         // index within the string.
  2040.         //
  2041.         public int LastIndexOf(string value)
  2042.         {
  2043.             return LastIndexOf(value, this.Length - 1, this.Length, StringComparison.CurrentCulture);
  2044.         }
  2045.        
  2046.         public int LastIndexOf(string value, int startIndex)
  2047.         {
  2048.             return LastIndexOf(value, startIndex, startIndex + 1, StringComparison.CurrentCulture);
  2049.         }
  2050.        
  2051.         public int LastIndexOf(string value, int startIndex, int count)
  2052.         {
  2053.             if (count < 0) {
  2054.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
  2055.             }
  2056.             return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
  2057.         }
  2058.        
  2059.         public int LastIndexOf(string value, StringComparison comparisonType)
  2060.         {
  2061.             return LastIndexOf(value, this.Length - 1, this.Length, comparisonType);
  2062.         }
  2063.        
  2064.         public int LastIndexOf(string value, int startIndex, StringComparison comparisonType)
  2065.         {
  2066.             return LastIndexOf(value, startIndex, startIndex + 1, comparisonType);
  2067.         }
  2068.        
  2069.         public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType)
  2070.         {
  2071.             if (value == null)
  2072.                 throw new ArgumentNullException("value");
  2073.            
  2074.             // Special case for 0 length input strings
  2075.             if (this.Length == 0 && (startIndex == -1 || startIndex == 0))
  2076.                 return (value.Length == 0) ? 0 : -1;
  2077.            
  2078.             // Make sure we're not out of range
  2079.             if (startIndex < 0 || startIndex > this.Length)
  2080.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  2081.            
  2082.             // Make sure that we allow startIndex == this.Length
  2083.             if (startIndex == this.Length) {
  2084.                 startIndex--;
  2085.                 if (count > 0)
  2086.                     count--;
  2087.                
  2088.                 // If we are looking for nothing, just return 0
  2089.                 if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0)
  2090.                     return startIndex;
  2091.             }
  2092.            
  2093.             // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
  2094.             if (count < 0 || startIndex - count + 1 < 0)
  2095.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
  2096.            
  2097.            
  2098.             switch (comparisonType) {
  2099.                 case StringComparison.CurrentCulture:
  2100.                     return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
  2101.                 case StringComparison.CurrentCultureIgnoreCase:
  2102.                    
  2103.                     return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
  2104.                 case StringComparison.InvariantCulture:
  2105.                    
  2106.                     return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None);
  2107.                 case StringComparison.InvariantCultureIgnoreCase:
  2108.                    
  2109.                     return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
  2110.                 case StringComparison.Ordinal:
  2111.                    
  2112.                     return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.Ordinal);
  2113.                 case StringComparison.OrdinalIgnoreCase:
  2114.                    
  2115.                     return TextInfo.LastIndexOfStringOrdinalIgnoreCase(this, value, startIndex, count);
  2116.                 default:
  2117.                    
  2118.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  2119.                     break;
  2120.             }
  2121.         }
  2122.        
  2123.         //
  2124.         //
  2125.         public string PadLeft(int totalWidth)
  2126.         {
  2127.             return PadHelper(totalWidth, ' ', false);
  2128.         }
  2129.        
  2130.         public string PadLeft(int totalWidth, char paddingChar)
  2131.         {
  2132.             return PadHelper(totalWidth, paddingChar, false);
  2133.         }
  2134.        
  2135.         public string PadRight(int totalWidth)
  2136.         {
  2137.             return PadHelper(totalWidth, ' ', true);
  2138.         }
  2139.        
  2140.         public string PadRight(int totalWidth, char paddingChar)
  2141.         {
  2142.             return PadHelper(totalWidth, paddingChar, true);
  2143.         }
  2144.        
  2145.        
  2146.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2147.         private extern string PadHelper(int totalWidth, char paddingChar, bool isRightPadded);
  2148.        
  2149.         // Determines whether a specified string is a prefix of the current instance
  2150.         //
  2151.         public bool StartsWith(string value)
  2152.         {
  2153.             return StartsWith(value, false, null);
  2154.         }
  2155.        
  2156.         [ComVisible(false)]
  2157.         public bool StartsWith(string value, StringComparison comparisonType)
  2158.         {
  2159.             if ((object)value == null) {
  2160.                 throw new ArgumentNullException("value");
  2161.             }
  2162.            
  2163.             if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) {
  2164.                 throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  2165.             }
  2166.            
  2167.             if ((object)this == (object)value) {
  2168.                 return true;
  2169.             }
  2170.            
  2171.             if (value.Length == 0) {
  2172.                 return true;
  2173.             }
  2174.            
  2175.             switch (comparisonType) {
  2176.                 case StringComparison.CurrentCulture:
  2177.                     return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
  2178.                 case StringComparison.CurrentCultureIgnoreCase:
  2179.                    
  2180.                     return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
  2181.                 case StringComparison.InvariantCulture:
  2182.                    
  2183.                     return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
  2184.                 case StringComparison.InvariantCultureIgnoreCase:
  2185.                    
  2186.                     return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
  2187.                 case StringComparison.Ordinal:
  2188.                    
  2189.                     if (this.Length < value.Length) {
  2190.                         return false;
  2191.                     }
  2192.                     return (nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0);
  2193.                 case StringComparison.OrdinalIgnoreCase:
  2194.                    
  2195.                     if (this.Length < value.Length) {
  2196.                         return false;
  2197.                     }
  2198.                    
  2199.                     return (TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length) == 0);
  2200.                 default:
  2201.                    
  2202.                     throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
  2203.                     break;
  2204.             }
  2205.         }
  2206.        
  2207.         public bool StartsWith(string value, bool ignoreCase, CultureInfo culture)
  2208.         {
  2209.             if (null == value) {
  2210.                 throw new ArgumentNullException("value");
  2211.             }
  2212.            
  2213.             if ((object)this == (object)value) {
  2214.                 return true;
  2215.             }
  2216.             CultureInfo referenceCulture = (culture == null) ? CultureInfo.CurrentCulture : culture;
  2217.             return referenceCulture.CompareInfo.IsPrefix(this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  2218.         }
  2219.        
  2220.         // Creates a copy of this string in lower case.
  2221.         public string ToLower()
  2222.         {
  2223.             return this.ToLower(CultureInfo.CurrentCulture);
  2224.         }
  2225.        
  2226.         // Creates a copy of this string in lower case. The culture is set by culture.
  2227.         public string ToLower(CultureInfo culture)
  2228.         {
  2229.             if (culture == null) {
  2230.                 throw new ArgumentNullException("culture");
  2231.             }
  2232.             return culture.TextInfo.ToLower(this);
  2233.         }
  2234.        
  2235.         // Creates a copy of this string in lower case based on invariant culture.
  2236.         public string ToLowerInvariant()
  2237.         {
  2238.             return this.ToLower(CultureInfo.InvariantCulture);
  2239.         }
  2240.        
  2241.         // Creates a copy of this string in upper case.
  2242.         public string ToUpper()
  2243.         {
  2244.             return this.ToUpper(CultureInfo.CurrentCulture);
  2245.         }
  2246.        
  2247.         // Creates a copy of this string in upper case. The culture is set by culture.
  2248.         public string ToUpper(CultureInfo culture)
  2249.         {
  2250.             if (culture == null) {
  2251.                 throw new ArgumentNullException("culture");
  2252.             }
  2253.             return culture.TextInfo.ToUpper(this);
  2254.         }
  2255.        
  2256.         //Creates a copy of this string in upper case based on invariant culture.
  2257.         public string ToUpperInvariant()
  2258.         {
  2259.             return this.ToUpper(CultureInfo.InvariantCulture);
  2260.         }
  2261.        
  2262.         // Returns this string.
  2263.         public override string ToString()
  2264.         {
  2265.             return this;
  2266.         }
  2267.        
  2268.         public string ToString(IFormatProvider provider)
  2269.         {
  2270.             return this;
  2271.         }
  2272.        
  2273.         // Method required for the ICloneable interface.
  2274.         // There's no point in cloning a string since they're immutable, so we simply return this.
  2275.         public object Clone()
  2276.         {
  2277.             return this;
  2278.         }
  2279.        
  2280.        
  2281.         // Trims the whitespace from both ends of the string. Whitespace is defined by
  2282.         // CharacterInfo.WhitespaceChars.
  2283.         //
  2284.         public string Trim()
  2285.         {
  2286.             return TrimHelper(WhitespaceChars, TrimBoth);
  2287.         }
  2288.        
  2289.         private string TrimHelper(char[] trimChars, int trimType)
  2290.         {
  2291.             //end will point to the first non-trimmed character on the right
  2292.             //start will point to the first non-trimmed character on the Left
  2293.             int end = this.Length - 1;
  2294.             int start = 0;
  2295.            
  2296.             //Trim specified characters.
  2297.             if (trimType != TrimTail) {
  2298.                 for (start = 0; start < this.Length; start++) {
  2299.                     int i = 0;
  2300.                     char ch = this[start];
  2301.                     for (i = 0; i < trimChars.Length; i++) {
  2302.                         if (trimChars[i] == ch)
  2303.                             break;
  2304.                     }
  2305.                     if (i == trimChars.Length) {
  2306.                         // the character is not white space
  2307.                         break;
  2308.                     }
  2309.                 }
  2310.             }
  2311.            
  2312.             if (trimType != TrimHead) {
  2313.                 for (end = Length - 1; end >= start; end--) {
  2314.                     int i = 0;
  2315.                     char ch = this[end];
  2316.                     for (i = 0; i < trimChars.Length; i++) {
  2317.                         if (trimChars[i] == ch)
  2318.                             break;
  2319.                     }
  2320.                     if (i == trimChars.Length) {
  2321.                         // the character is not white space
  2322.                         break;
  2323.                     }
  2324.                 }
  2325.             }
  2326.            
  2327.             //Create a new STRINGREF and initialize it from the range determined above.
  2328.             int len = end - start + 1;
  2329.             if (len == this.Length) {
  2330.                 // Don't allocate a new string is the trimmed string has not changed.
  2331.                 return this;
  2332.             }
  2333.             else {
  2334.                 if (len == 0) {
  2335.                     return String.Empty;
  2336.                 }
  2337.                 return InternalSubString(start, len, false);
  2338.             }
  2339.         }
  2340.         //
  2341.         //
  2342.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2343.         public extern string Insert(int startIndex, string value);
  2344.        
  2345.         // Replaces all instances of oldChar with newChar.
  2346.         //
  2347.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2348.         public extern string Replace(char oldChar, char newChar);
  2349.        
  2350.         // This method contains the same functionality as StringBuilder Replace. The only difference is that
  2351.         // a new String has to be allocated since Strings are immutable
  2352.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2353.         public extern string Replace(string oldValue, string newValue);
  2354.        
  2355.         //
  2356.         //
  2357.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2358.         public extern string Remove(int startIndex, int count);
  2359.        
  2360.        
  2361.         // a remove that just takes a startindex.
  2362.         public string Remove(int startIndex)
  2363.         {
  2364.             if (startIndex < 0) {
  2365.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  2366.             }
  2367.            
  2368.             if (startIndex >= Length) {
  2369.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndexLessThanLength"));
  2370.             }
  2371.            
  2372.             return Substring(0, startIndex);
  2373.         }
  2374.        
  2375.         public static string Format(string format, object arg0)
  2376.         {
  2377.             return Format(null, format, new object[] {arg0});
  2378.         }
  2379.        
  2380.         public static string Format(string format, object arg0, object arg1)
  2381.         {
  2382.             return Format(null, format, new object[] {arg0, arg1});
  2383.         }
  2384.        
  2385.         public static string Format(string format, object arg0, object arg1, object arg2)
  2386.         {
  2387.             return Format(null, format, new object[] {arg0, arg1, arg2});
  2388.         }
  2389.        
  2390.        
  2391.         public static string Format(string format, params object[] args)
  2392.         {
  2393.             return Format(null, format, args);
  2394.         }
  2395.        
  2396.         public static string Format(IFormatProvider provider, string format, params object[] args)
  2397.         {
  2398.             if (format == null || args == null)
  2399.                 throw new ArgumentNullException((format == null) ? "format" : "args");
  2400.             StringBuilder sb = new StringBuilder(format.Length + args.Length * 8);
  2401.             sb.AppendFormat(provider, format, args);
  2402.             return sb.ToString();
  2403.         }
  2404.        
  2405.         unsafe public static string Copy(string str)
  2406.         {
  2407.             if (str == null) {
  2408.                 throw new ArgumentNullException("str");
  2409.             }
  2410.            
  2411.             int length = str.Length;
  2412.            
  2413.             string result = FastAllocateString(length);
  2414.            
  2415.             fixed (char* dest = &result.m_firstChar)
  2416.                 fixed (char* src = &str.m_firstChar)
  2417.                     wstrcpyPtrAligned(dest, src, length);
  2418.            
  2419.             return result;
  2420.         }
  2421.        
  2422.         // Used by StringBuilder to avoid data corruption
  2423.         unsafe static internal string InternalCopy(string str)
  2424.         {
  2425.             int length = str.Length;
  2426.             string result = FastAllocateString(length);
  2427.            
  2428.             // The underlying's String can changed length is StringBuilder
  2429.             fixed (char* dest = &result.m_firstChar)
  2430.                 fixed (char* src = &str.m_firstChar)
  2431.                     wstrcpyPtrAligned(dest, src, length);
  2432.            
  2433.             return result;
  2434.         }
  2435.        
  2436.         public static string Concat(object arg0)
  2437.         {
  2438.             if (arg0 == null) {
  2439.                 return String.Empty;
  2440.             }
  2441.             return arg0.ToString();
  2442.         }
  2443.        
  2444.         public static string Concat(object arg0, object arg1)
  2445.         {
  2446.             if (arg0 == null) {
  2447.                 arg0 = String.Empty;
  2448.             }
  2449.            
  2450.             if (arg1 == null) {
  2451.                 arg1 = String.Empty;
  2452.             }
  2453.             return Concat(arg0.ToString(), arg1.ToString());
  2454.         }
  2455.        
  2456.         public static string Concat(object arg0, object arg1, object arg2)
  2457.         {
  2458.             if (arg0 == null) {
  2459.                 arg0 = String.Empty;
  2460.             }
  2461.            
  2462.             if (arg1 == null) {
  2463.                 arg1 = String.Empty;
  2464.             }
  2465.            
  2466.             if (arg2 == null) {
  2467.                 arg2 = String.Empty;
  2468.             }
  2469.            
  2470.             return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString());
  2471.         }
  2472.        
  2473.         [CLSCompliant(false)]
  2474.         public static string Concat(object arg0, object arg1, object arg2, object arg3, __arglist __arglist)
  2475.         {
  2476.             object[] objArgs;
  2477.             int argCount;
  2478.            
  2479.             ArgIterator args = new ArgIterator(__arglist);
  2480.            
  2481.             //+4 to account for the 4 hard-coded arguments at the beginning of the list.
  2482.             argCount = args.GetRemainingCount() + 4;
  2483.            
  2484.             objArgs = new object[argCount];
  2485.            
  2486.             //Handle the hard-coded arguments
  2487.             objArgs[0] = arg0;
  2488.             objArgs[1] = arg1;
  2489.             objArgs[2] = arg2;
  2490.             objArgs[3] = arg3;
  2491.            
  2492.             //Walk all of the args in the variable part of the argument list.
  2493.             for (int i = 4; i < argCount; i++) {
  2494.                 objArgs[i] = TypedReference.ToObject(args.GetNextArg());
  2495.             }
  2496.            
  2497.             return Concat(objArgs);
  2498.         }
  2499.        
  2500.        
  2501.         public static string Concat(params object[] args)
  2502.         {
  2503.             if (args == null) {
  2504.                 throw new ArgumentNullException("args");
  2505.             }
  2506.            
  2507.             string[] sArgs = new string[args.Length];
  2508.             int totalLength = 0;
  2509.            
  2510.             for (int i = 0; i < args.Length; i++) {
  2511.                 object value = args[i];
  2512.                 sArgs[i] = ((value == null) ? (String.Empty) : (value.ToString()));
  2513.                 totalLength += sArgs[i].Length;
  2514.                 // check for overflow
  2515.                 if (totalLength < 0) {
  2516.                     throw new OutOfMemoryException();
  2517.                 }
  2518.             }
  2519.             return ConcatArray(sArgs, totalLength);
  2520.         }
  2521.        
  2522.        
  2523.         public static string Concat(string str0, string str1)
  2524.         {
  2525.             if (IsNullOrEmpty(str0)) {
  2526.                 if (IsNullOrEmpty(str1)) {
  2527.                     return String.Empty;
  2528.                 }
  2529.                 return str1;
  2530.             }
  2531.            
  2532.             if (IsNullOrEmpty(str1)) {
  2533.                 return str0;
  2534.             }
  2535.            
  2536.             int str0Length = str0.Length;
  2537.            
  2538.             string result = FastAllocateString(str0Length + str1.Length);
  2539.            
  2540.             FillStringChecked(result, 0, str0);
  2541.             FillStringChecked(result, str0Length, str1);
  2542.            
  2543.             return result;
  2544.         }
  2545.        
  2546.         public static string Concat(string str0, string str1, string str2)
  2547.         {
  2548.             if (str0 == null && str1 == null && str2 == null) {
  2549.                 return String.Empty;
  2550.             }
  2551.            
  2552.             if (str0 == null) {
  2553.                 str0 = String.Empty;
  2554.             }
  2555.            
  2556.             if (str1 == null) {
  2557.                 str1 = String.Empty;
  2558.             }
  2559.            
  2560.             if (str2 == null) {
  2561.                 str2 = String.Empty;
  2562.             }
  2563.            
  2564.             int totalLength = str0.Length + str1.Length + str2.Length;
  2565.            
  2566.             string result = FastAllocateString(totalLength);
  2567.             FillStringChecked(result, 0, str0);
  2568.             FillStringChecked(result, str0.Length, str1);
  2569.             FillStringChecked(result, str0.Length + str1.Length, str2);
  2570.            
  2571.             return result;
  2572.         }
  2573.        
  2574.         public static string Concat(string str0, string str1, string str2, string str3)
  2575.         {
  2576.             if (str0 == null && str1 == null && str2 == null && str3 == null) {
  2577.                 return String.Empty;
  2578.             }
  2579.            
  2580.             if (str0 == null) {
  2581.                 str0 = String.Empty;
  2582.             }
  2583.            
  2584.             if (str1 == null) {
  2585.                 str1 = String.Empty;
  2586.             }
  2587.            
  2588.             if (str2 == null) {
  2589.                 str2 = String.Empty;
  2590.             }
  2591.            
  2592.             if (str3 == null) {
  2593.                 str3 = String.Empty;
  2594.             }
  2595.            
  2596.             int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;
  2597.            
  2598.             string result = FastAllocateString(totalLength);
  2599.             FillStringChecked(result, 0, str0);
  2600.             FillStringChecked(result, str0.Length, str1);
  2601.             FillStringChecked(result, str0.Length + str1.Length, str2);
  2602.             FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3);
  2603.            
  2604.             return result;
  2605.         }
  2606.        
  2607.         private static string ConcatArray(string[] values, int totalLength)
  2608.         {
  2609.             string result = FastAllocateString(totalLength);
  2610.             int currPos = 0;
  2611.            
  2612.             for (int i = 0; i < values.Length; i++) {
  2613.                 BCLDebug.Assert((currPos <= totalLength - values[i].Length), "[String.ConcatArray](currPos <= totalLength - values[i].Length)");
  2614.                
  2615.                 FillStringChecked(result, currPos, values[i]);
  2616.                 currPos += values[i].Length;
  2617.             }
  2618.            
  2619.             return result;
  2620.         }
  2621.        
  2622.         public static string Concat(params string[] values)
  2623.         {
  2624.             int totalLength = 0;
  2625.            
  2626.             if (values == null) {
  2627.                 throw new ArgumentNullException("values");
  2628.             }
  2629.            
  2630.             // Always make a copy to prevent changing the array on another thread.
  2631.             string[] internalValues = new string[values.Length];
  2632.            
  2633.             for (int i = 0; i < values.Length; i++) {
  2634.                 string value = values[i];
  2635.                 internalValues[i] = ((value == null) ? (String.Empty) : (value));
  2636.                 totalLength += internalValues[i].Length;
  2637.                 // check for overflow
  2638.                 if (totalLength < 0) {
  2639.                     throw new OutOfMemoryException();
  2640.                 }
  2641.             }
  2642.            
  2643.             return ConcatArray(internalValues, totalLength);
  2644.         }
  2645.        
  2646.         public static string Intern(string str)
  2647.         {
  2648.             if (str == null) {
  2649.                 throw new ArgumentNullException("str");
  2650.             }
  2651.             return Thread.GetDomain().GetOrInternString(str);
  2652.         }
  2653.        
  2654.         public static string IsInterned(string str)
  2655.         {
  2656.             if (str == null) {
  2657.                 throw new ArgumentNullException("str");
  2658.             }
  2659.             return Thread.GetDomain().IsStringInterned(str);
  2660.         }
  2661.        
  2662.        
  2663.         //
  2664.         // IValue implementation
  2665.         //
  2666.        
  2667.         public TypeCode GetTypeCode()
  2668.         {
  2669.             return TypeCode.String;
  2670.         }
  2671.        
  2672.         /// <internalonly/>
  2673.         bool IConvertible.ToBoolean(IFormatProvider provider)
  2674.         {
  2675.             return Convert.ToBoolean(this, provider);
  2676.         }
  2677.        
  2678.         /// <internalonly/>
  2679.         char IConvertible.ToChar(IFormatProvider provider)
  2680.         {
  2681.             return Convert.ToChar(this, provider);
  2682.         }
  2683.        
  2684.         /// <internalonly/>
  2685.         sbyte IConvertible.ToSByte(IFormatProvider provider)
  2686.         {
  2687.             return Convert.ToSByte(this, provider);
  2688.         }
  2689.        
  2690.         /// <internalonly/>
  2691.         byte IConvertible.ToByte(IFormatProvider provider)
  2692.         {
  2693.             return Convert.ToByte(this, provider);
  2694.         }
  2695.        
  2696.         /// <internalonly/>
  2697.         short IConvertible.ToInt16(IFormatProvider provider)
  2698.         {
  2699.             return Convert.ToInt16(this, provider);
  2700.         }
  2701.        
  2702.         /// <internalonly/>
  2703.         ushort IConvertible.ToUInt16(IFormatProvider provider)
  2704.         {
  2705.             return Convert.ToUInt16(this, provider);
  2706.         }
  2707.        
  2708.         /// <internalonly/>
  2709.         int IConvertible.ToInt32(IFormatProvider provider)
  2710.         {
  2711.             return Convert.ToInt32(this, provider);
  2712.         }
  2713.        
  2714.         /// <internalonly/>
  2715.         uint IConvertible.ToUInt32(IFormatProvider provider)
  2716.         {
  2717.             return Convert.ToUInt32(this, provider);
  2718.         }
  2719.        
  2720.         /// <internalonly/>
  2721.         long IConvertible.ToInt64(IFormatProvider provider)
  2722.         {
  2723.             return Convert.ToInt64(this, provider);
  2724.         }
  2725.        
  2726.         /// <internalonly/>
  2727.         ulong IConvertible.ToUInt64(IFormatProvider provider)
  2728.         {
  2729.             return Convert.ToUInt64(this, provider);
  2730.         }
  2731.        
  2732.         /// <internalonly/>
  2733.         float IConvertible.ToSingle(IFormatProvider provider)
  2734.         {
  2735.             return Convert.ToSingle(this, provider);
  2736.         }
  2737.        
  2738.         /// <internalonly/>
  2739.         double IConvertible.ToDouble(IFormatProvider provider)
  2740.         {
  2741.             return Convert.ToDouble(this, provider);
  2742.         }
  2743.        
  2744.         /// <internalonly/>
  2745.         decimal IConvertible.ToDecimal(IFormatProvider provider)
  2746.         {
  2747.             return Convert.ToDecimal(this, provider);
  2748.         }
  2749.        
  2750.         /// <internalonly/>
  2751.         DateTime IConvertible.ToDateTime(IFormatProvider provider)
  2752.         {
  2753.             return Convert.ToDateTime(this, provider);
  2754.         }
  2755.        
  2756.         /// <internalonly/>
  2757.         object IConvertible.ToType(Type type, IFormatProvider provider)
  2758.         {
  2759.             return Convert.DefaultToType((IConvertible)this, type, provider);
  2760.         }
  2761.        
  2762.         // Is this a string that can be compared quickly (that is it has only characters > 0x80
  2763.         // and not a - or '
  2764.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2765.         internal extern bool IsFastSort();
  2766.         // Is this a string that only contains characters < 0x80.
  2767.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2768.         internal extern bool IsAscii();
  2769.        
  2770.         ///<internalonly/>
  2771.         unsafe internal void SetChar(int index, char value)
  2772.         {
  2773.             #if _DEBUG
  2774.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2775.             #endif
  2776.            
  2777.             //Bounds check and then set the actual bit.
  2778.             if ((UInt32)index >= (UInt32)Length)
  2779.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  2780.            
  2781.             fixed (char* p = &this.m_firstChar) {
  2782.                 // Set the character.
  2783.                 p[index] = value;
  2784.             }
  2785.         }
  2786.        
  2787.         #if _DEBUG
  2788.         // Only used in debug build. Insure that the HighChar state information for a string is not set as known
  2789.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  2790.         private extern bool ValidModifiableString();
  2791.         #endif
  2792.        
  2793.         ///<internalonly/>
  2794.         unsafe internal void AppendInPlace(char value, int currentLength)
  2795.         {
  2796.             BCLDebug.Assert(currentLength < m_arrayLength, "[String.AppendInPlace]currentLength < m_arrayLength");
  2797.             #if _DEBUG
  2798.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2799.             #endif
  2800.            
  2801.             fixed (char* p = &this.m_firstChar) {
  2802.                 // Append the character.
  2803.                 p[currentLength] = value;
  2804.                 currentLength++;
  2805.                 p[currentLength] = '\0';
  2806.                 m_stringLength = currentLength;
  2807.             }
  2808.         }
  2809.        
  2810.        
  2811.         ///<internalonly/>
  2812.         unsafe internal void AppendInPlace(char value, int repeatCount, int currentLength)
  2813.         {
  2814.             int newLength = currentLength + repeatCount;
  2815.            
  2816.             BCLDebug.Assert(newLength < m_arrayLength, "[String.AppendInPlace]currentLength+repeatCount < m_arrayLength");
  2817.             #if _DEBUG
  2818.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2819.             #endif
  2820.            
  2821.             fixed (char* p = &this.m_firstChar) {
  2822.                 int i;
  2823.                 for (i = currentLength; i < newLength; i++) {
  2824.                     p[i] = value;
  2825.                 }
  2826.                 p[i] = '\0';
  2827.             }
  2828.             this.m_stringLength = newLength;
  2829.         }
  2830.        
  2831.         ///<internalonly/>
  2832.         unsafe internal void AppendInPlace(string value, int currentLength)
  2833.         {
  2834.             int count = value.Length;
  2835.             int newLength = currentLength + count;
  2836.            
  2837.             BCLDebug.Assert(value != null, "[String.AppendInPlace]value!=null");
  2838.             BCLDebug.Assert(newLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong.");
  2839.             #if _DEBUG
  2840.             // BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2841.             #endif
  2842.            
  2843.             fixed (char* dest = &this.m_firstChar) {
  2844.                 fixed (char* src = &value.m_firstChar) {
  2845.                     wstrcpy(dest + currentLength, src, count);
  2846.                 }
  2847.                 dest[newLength] = '\0';
  2848.             }
  2849.             this.m_stringLength = newLength;
  2850.         }
  2851.        
  2852.         unsafe internal void AppendInPlace(string value, int startIndex, int count, int currentLength)
  2853.         {
  2854.             int newLength = currentLength + count;
  2855.            
  2856.             BCLDebug.Assert(value != null, "[String.AppendInPlace]value!=null");
  2857.             BCLDebug.Assert(newLength < this.m_arrayLength, "[String.AppendInPlace]newLength < this.m_arrayLength");
  2858.             BCLDebug.Assert(count >= 0, "[String.AppendInPlace]count>=0");
  2859.             BCLDebug.Assert(startIndex >= 0, "[String.AppendInPlace]startIndex>=0");
  2860.             BCLDebug.Assert(startIndex <= (value.Length - count), "[String.AppendInPlace]startIndex <= (value.Length - count)");
  2861.             #if _DEBUG
  2862.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2863.             #endif
  2864.            
  2865.             fixed (char* dest = &this.m_firstChar) {
  2866.                 fixed (char* src = &value.m_firstChar)
  2867.                     wstrcpy(dest + currentLength, src + startIndex, count);
  2868.                 dest[newLength] = '\0';
  2869.             }
  2870.             this.m_stringLength = newLength;
  2871.         }
  2872.        
  2873.         unsafe internal void AppendInPlace(char* value, int count, int currentLength)
  2874.         {
  2875.             int newLength = currentLength + count;
  2876.            
  2877.             BCLDebug.Assert(value != null, "[String.AppendInPlace]value!=null");
  2878.             BCLDebug.Assert(newLength < this.m_arrayLength, "[String.AppendInPlace]newLength < this.m_arrayLength");
  2879.             BCLDebug.Assert(count >= 0, "[String.AppendInPlace]count>=0");
  2880.             #if _DEBUG
  2881.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2882.             #endif
  2883.            
  2884.             fixed (char* p = &this.m_firstChar) {
  2885.                 wstrcpy(p + currentLength, value, count);
  2886.                 p[newLength] = '\0';
  2887.             }
  2888.             this.m_stringLength = newLength;
  2889.         }
  2890.        
  2891.        
  2892.         ///<internalonly/>
  2893.         unsafe internal void AppendInPlace(char[] value, int start, int count, int currentLength)
  2894.         {
  2895.             int newLength = currentLength + count;
  2896.            
  2897.             BCLDebug.Assert(value != null, "[String.AppendInPlace]value!=null");
  2898.            
  2899.             BCLDebug.Assert(newLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong.");
  2900.             BCLDebug.Assert(value.Length - count >= start, "[String.AppendInPlace]value.Length-count>=start");
  2901.             #if _DEBUG
  2902.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2903.             #endif
  2904.            
  2905.             fixed (char* dest = &this.m_firstChar) {
  2906.                 // Note: fixed does not like empty arrays
  2907.                 if (count > 0)
  2908.                     fixed (char* src = value)
  2909.                         wstrcpy(dest + currentLength, src + start, count);
  2910.                 dest[newLength] = '\0';
  2911.             }
  2912.             this.m_stringLength = newLength;
  2913.         }
  2914.        
  2915.        
  2916.         ///<internalonly/>
  2917.         unsafe internal void ReplaceCharInPlace(char oldChar, char newChar, int startIndex, int count, int currentLength)
  2918.         {
  2919.             BCLDebug.Assert(startIndex >= 0, "[String.ReplaceCharInPlace]startIndex>0");
  2920.             BCLDebug.Assert(startIndex <= currentLength, "[String.ReplaceCharInPlace]startIndex>=Length");
  2921.             BCLDebug.Assert((startIndex <= currentLength - count), "[String.ReplaceCharInPlace]count>0 && startIndex<=currentLength-count");
  2922.             #if _DEBUG
  2923.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  2924.             #endif
  2925.            
  2926.             int endIndex = startIndex + count;
  2927.            
  2928.             fixed (char* p = &this.m_firstChar) {
  2929.                 for (int i = startIndex; i < endIndex; i++) {
  2930.                     if (p[i] == oldChar) {
  2931.                         p[i] = newChar;
  2932.                     }
  2933.                 }
  2934.             }
  2935.         }
  2936.        
  2937.         ///<internalonly/>
  2938.         static internal string GetStringForStringBuilder(string value, int capacity)
  2939.         {
  2940.             BCLDebug.Assert(value != null, "[String.GetStringForStringBuilder]value!=null");
  2941.             return GetStringForStringBuilder(value, 0, value.Length, capacity);
  2942.         }
  2943.        
  2944.         ///<internalonly/>
  2945.         unsafe static internal string GetStringForStringBuilder(string value, int startIndex, int length, int capacity)
  2946.         {
  2947.             BCLDebug.Assert(value != null, "[String.GetStringForStringBuilder]value!=null");
  2948.             BCLDebug.Assert(capacity >= length, "[String.GetStringForStringBuilder]capacity>=length");
  2949.            
  2950.             string newStr = FastAllocateString(capacity);
  2951.             if (value.Length == 0) {
  2952.                 newStr.SetLength(0);
  2953.                 // already null terminated
  2954.                 return newStr;
  2955.             }
  2956.             fixed (char* dest = &newStr.m_firstChar)
  2957.                 fixed (char* src = &value.m_firstChar) {
  2958.                     wstrcpy(dest, src + startIndex, length);
  2959.                 }
  2960.             newStr.SetLength(length);
  2961.             // already null terminated
  2962.             return newStr;
  2963.         }
  2964.        
  2965.         ///<internalonly/>
  2966.         unsafe private void NullTerminate()
  2967.         {
  2968.             fixed (char* p = &this.m_firstChar) {
  2969.                 p[m_stringLength] = '\0';
  2970.             }
  2971.         }
  2972.        
  2973.         ///<internalonly/>
  2974.         unsafe internal void ClearPostNullChar()
  2975.         {
  2976.             int newLength = Length + 1;
  2977.             if (newLength < m_arrayLength) {
  2978.                 fixed (char* p = &this.m_firstChar) {
  2979.                     p[newLength] = '\0';
  2980.                 }
  2981.             }
  2982.         }
  2983.        
  2984.         ///<internalonly/>
  2985.         internal void SetLength(int newLength)
  2986.         {
  2987.             BCLDebug.Assert(newLength <= m_arrayLength, "newLength<=m_arrayLength");
  2988.             m_stringLength = newLength;
  2989.         }
  2990.        
  2991.        
  2992.        
  2993.         public CharEnumerator GetEnumerator()
  2994.         {
  2995.             BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead.");
  2996.             return new CharEnumerator(this);
  2997.         }
  2998.        
  2999.         IEnumerator<char> IEnumerable<char>.GetEnumerator()
  3000.         {
  3001.             BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead.");
  3002.             return new CharEnumerator(this);
  3003.         }
  3004.        
  3005.         /// <internalonly/>
  3006.         IEnumerator IEnumerable.GetEnumerator()
  3007.         {
  3008.             BCLDebug.Perf(false, "Avoid using String's CharEnumerator until C# special cases foreach on String - use the indexed property on String instead.");
  3009.             return new CharEnumerator(this);
  3010.         }
  3011.        
  3012.         unsafe internal void InternalSetCharNoBoundsCheck(int index, char value)
  3013.         {
  3014.             fixed (char* p = &this.m_firstChar) {
  3015.                 p[index] = value;
  3016.             }
  3017.         }
  3018.        
  3019.         // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes.
  3020.         unsafe static internal void InternalCopy(string src, IntPtr dest, int len)
  3021.         {
  3022.             if (len == 0)
  3023.                 return;
  3024.             fixed (char* charPtr = &src.m_firstChar) {
  3025.                 byte* srcPtr = (byte*)charPtr;
  3026.                 byte* dstPtr = (byte*)dest.ToPointer();
  3027.                 Buffer.memcpyimpl(srcPtr, dstPtr, len);
  3028.             }
  3029.         }
  3030.        
  3031.         // memcopies characters inside a String.
  3032.         unsafe static internal void InternalMemCpy(string src, int srcOffset, string dst, int destOffset, int len)
  3033.         {
  3034.             if (len == 0)
  3035.                 return;
  3036.             fixed (char* srcPtr = &src.m_firstChar) {
  3037.                 fixed (char* dstPtr = &dst.m_firstChar) {
  3038.                     Buffer.memcpyimpl((byte*)(srcPtr + srcOffset), (byte*)(dstPtr + destOffset), len);
  3039.                 }
  3040.             }
  3041.         }
  3042.        
  3043.        
  3044.         unsafe static internal void revmemcpyimpl(byte* src, byte* dest, int len)
  3045.         {
  3046.             dest += len;
  3047.             src += len;
  3048.             while (len-- > 0) {
  3049.                 dest--;
  3050.                 src--;
  3051.                 *dest = *src;
  3052.             }
  3053.         }
  3054.        
  3055.        
  3056.        
  3057.         unsafe internal void InsertInPlace(int index, string value, int repeatCount, int currentLength, int requiredLength)
  3058.         {
  3059.             BCLDebug.Assert(requiredLength < m_arrayLength, "[String.InsertString] requiredLength < m_arrayLength");
  3060.             BCLDebug.Assert(index >= 0, "index >= 0");
  3061.             BCLDebug.Assert(value.Length * repeatCount < m_arrayLength - index, "[String.InsertString] value.Length * repeatCount < m_arrayLength - index");
  3062.             #if _DEBUG
  3063.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  3064.             #endif
  3065.             //Copy the old characters over to make room and then insert the new characters.
  3066.             fixed (char* srcPtr = &this.m_firstChar) {
  3067.                 fixed (char* valuePtr = &value.m_firstChar) {
  3068.                     revmemcpyimpl((byte*)(srcPtr + index), (byte*)(srcPtr + index + value.Length * repeatCount), (currentLength - index) * sizeof(char));
  3069.                     for (int i = 0; i < repeatCount; i++) {
  3070.                         Buffer.memcpyimpl((byte*)valuePtr, (byte*)(srcPtr + index + i * value.Length), value.Length * sizeof(char));
  3071.                     }
  3072.                 }
  3073.             }
  3074.             SetLength(requiredLength);
  3075.             NullTerminate();
  3076.         }
  3077.        
  3078.         // Note InsertInPlace char[] has slightly different semantics than the one that takes a String.
  3079.         unsafe internal void InsertInPlace(int index, char[] value, int startIndex, int charCount, int currentLength, int requiredLength)
  3080.         {
  3081.             BCLDebug.Assert(requiredLength < m_arrayLength, "[String.InsertInPlace] requiredLength < m_arrayLength");
  3082.             BCLDebug.Assert(startIndex >= 0 && index >= 0, "startIndex >= 0 && index >= 0");
  3083.             BCLDebug.Assert(charCount <= value.Length - startIndex, "[String.InsertInPlace] charCount <= value.Length - startIndex");
  3084.             BCLDebug.Assert(charCount < m_arrayLength - index, "[String.InsertInPlace]charCount < m_arrayLength - index");
  3085.             #if _DEBUG
  3086.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  3087.             #endif
  3088.             //Copy the old characters over to make room and then insert the new characters.
  3089.             fixed (char* srcPtr = &this.m_firstChar) {
  3090.                 fixed (char* valuePtr = value) {
  3091.                     revmemcpyimpl((byte*)(srcPtr + index), (byte*)(srcPtr + index + charCount), (currentLength - index) * sizeof(char));
  3092.                     Buffer.memcpyimpl((byte*)(valuePtr + startIndex), (byte*)(srcPtr + index), charCount * sizeof(char));
  3093.                 }
  3094.             }
  3095.             SetLength(requiredLength);
  3096.             NullTerminate();
  3097.         }
  3098.        
  3099.         unsafe internal void RemoveInPlace(int index, int charCount, int currentLength)
  3100.         {
  3101.             BCLDebug.Assert(index >= 0, "index >= 0");
  3102.             BCLDebug.Assert(charCount < m_arrayLength - index, "[String.InsertInPlace]charCount < m_arrayLength - index");
  3103.             #if _DEBUG
  3104.             BCLDebug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set");
  3105.             #endif
  3106.             //Move the remaining characters to the left and set the string length.
  3107.             String.InternalMemCpy(this, index + charCount, this, index, (currentLength - charCount - index) * sizeof(char));
  3108.             int newLength = currentLength - charCount;
  3109.             SetLength(newLength);
  3110.             NullTerminate();
  3111.             //Null terminate.
  3112.         }
  3113.     }
  3114.    
  3115.     [ComVisible(false)]
  3116.     [Flags()]
  3117.     public enum StringSplitOptions
  3118.     {
  3119.         None = 0,
  3120.         RemoveEmptyEntries = 1
  3121.     }
  3122. }

Developer Fusion