remove warning
[mono.git] / mcs / class / corlib / System / String.cs
index eb09228acf4fa6693c2772ebca074561fbb5409d..df5b4451f3e772ea0dca6578dce2014ac27318ec 100644 (file)
@@ -6,9 +6,10 @@
 //   Jeffrey Stedfast (fejj@ximian.com)
 //   Dan Lewis (dihlewis@yahoo.co.uk)
 //   Sebastien Pouliot  <sebastien@ximian.com>
+//   Marek Safar (marek.safar@seznam.cz)
 //
 // (C) 2001 Ximian, Inc.  http://www.ximian.com
-// Copyright (C) 2004 Novell (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Text;
 using System.Collections;
 using System.Globalization;
 using System.Runtime.CompilerServices;
+
+#if NET_2_0
+using System.Collections.Generic;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
 using Mono.Globalization.Unicode;
+#endif
 
 namespace System
 {
        [Serializable]
-       public sealed class String : IConvertible, ICloneable, IEnumerable,
 #if NET_2_0
-               IComparable, IComparable<String>
+       [ComVisible (true)]
+       public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
 #else
-               IComparable
+       public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
 #endif
        {
                [NonSerialized] private int length;
@@ -69,28 +75,42 @@ namespace System
                        if (len != b.length)
                                return false;
 
-                       if (len == 0)
-                               return true;
+                       fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
+                               char* s1_ptr = s1;
+                               char* s2_ptr = s2;
 
-                       fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
-                               // it must be one char, because 0 len is done above
-                               if (len < 2)
-                                       return *s1 == *s2;
+                               while (len >= 8) {
+                                       if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
+                                               ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
+                                               ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
+                                               ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
+                                               return false;
 
-                               // check by twos
-                               int * sint1 = (int *) s1, sint2 = (int *) s2;
-                               int n2 = len >> 1;
-                               do {
-                                       if (*sint1++ != *sint2++)
+                                       s1_ptr += 8;
+                                       s2_ptr += 8;
+                                       len -= 8;
+                               }
+
+                               if (len >= 4) {
+                                       if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
+                                               ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
                                                return false;
-                               } while (--n2 != 0);
 
-                               // nothing left
-                               if ((len & 1) == 0)
-                                       return true;
+                                       s1_ptr += 4;
+                                       s2_ptr += 4;
+                                       len -= 4;
+                               }
+
+                               if (len > 1) {
+                                       if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
+                                               return false;
+
+                                       s1_ptr += 2;
+                                       s2_ptr += 2;
+                                       len -= 2;
+                               }
 
-                               // check the last one
-                               return *(char *) sint1 == *(char *) sint2;
+                               return len == 0 || *s1_ptr == *s2_ptr;
                        }
                }
 
@@ -104,11 +124,17 @@ namespace System
                        return !Equals (a, b);
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
                public override bool Equals (Object obj)
                {
                        return Equals (this, obj as String);
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
                public bool Equals (String value)
                {
                        return Equals (this, value);
@@ -190,11 +216,118 @@ namespace System
                        if (count == 1) 
                                return new String[1] { ToString() };
 
-                       return InternalSplit (separator, count);
+                       return InternalSplit (separator, count, 0);
                }
 
+#if NET_2_0
+               [ComVisible (false)]
+               [MonoDocumentationNote ("code should be moved to managed")]
+               public String[] Split (char[] separator, int count, StringSplitOptions options)
+               {
+                       if (separator == null || separator.Length == 0)
+                               return Split (WhiteChars, count, options);
+
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+                       if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+                               throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
+
+                       if (count == 0)
+                               return new string [0];
+
+                       return InternalSplit (separator, count, (int)options);
+               }
+
+               [ComVisible (false)]
+               public String[] Split (string[] separator, int count, StringSplitOptions options)
+               {
+                       if (separator == null || separator.Length == 0)
+                               return Split (WhiteChars, count, options);
+
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+                       if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+                               throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
+
+                       bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
+
+                       if (count == 0 || (this == String.Empty && removeEmpty))
+                               return new String [0];
+
+                       ArrayList arr = new ArrayList ();
+
+                       int pos = 0;
+                       while (pos < this.Length) {
+                               int matchIndex = -1;
+                               int matchPos = Int32.MaxValue;
+
+                               // Find the first position where any of the separators matches
+                               for (int i = 0; i < separator.Length; ++i) {
+                                       string sep = separator [i];
+                                       if (sep == null || sep == String.Empty)
+                                               continue;
+
+                                       int match = IndexOf (sep, pos);
+                                       if (match > -1 && match < matchPos) {
+                                               matchIndex = i;
+                                               matchPos = match;
+                                       }
+                               }
+
+                               if (matchIndex == -1)
+                                       break;
+
+                               if (matchPos == pos && removeEmpty) {
+                                       pos = matchPos + separator [matchIndex].Length;
+                               }
+                               else {
+                                       arr.Add (this.Substring (pos, matchPos - pos));
+
+                                       pos = matchPos + separator [matchIndex].Length;
+
+                                       if (arr.Count == count - 1) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (arr.Count == 0)
+                               return new String [] { this };
+                       else {
+                               if (removeEmpty && pos == this.Length) {
+                                       String[] res = new String [arr.Count];
+                                       arr.CopyTo (0, res, 0, arr.Count);
+
+                                       return res;
+                               }
+                               else {
+                                       String[] res = new String [arr.Count + 1];
+                                       arr.CopyTo (0, res, 0, arr.Count);
+                                       res [arr.Count] = this.Substring (pos);
+
+                                       return res;
+                               }
+                       }
+               }
+
+               [ComVisible (false)]
+               public String[] Split (char[] separator, StringSplitOptions options)
+               {
+                       return Split (separator, Int32.MaxValue, options);
+               }
+
+               [ComVisible (false)]
+               public String[] Split (String[] separator, StringSplitOptions options)
+               {
+                       return Split (separator, Int32.MaxValue, options);
+               }
+#endif
+
                public unsafe String Substring (int startIndex)
                {
+                       if (startIndex == 0)
+                               return this;
+
                        if (startIndex < 0 || startIndex > this.length)
                                throw new ArgumentOutOfRangeException ("startIndex");
 
@@ -230,10 +363,18 @@ namespace System
                }       
 
                private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
+#if NET_2_0
+                       (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
+#endif
                        (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
                        (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
                        (char) 0x3000, (char) 0xFEFF };
 
+               public String Trim ()
+               {
+                       return InternalTrim (WhiteChars, 0);
+               }
+
                public String Trim (params char[] trimChars)
                {
                        if (trimChars == null || trimChars.Length == 0)
@@ -351,7 +492,59 @@ namespace System
 
                        return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
                }
+#if NET_2_0
+               public static int Compare (string strA, string strB, StringComparison comparisonType)
+               {
+                       switch (comparisonType) {
+                       case StringComparison.CurrentCulture:
+                               return Compare (strA, strB, false, CultureInfo.CurrentCulture);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return Compare (strA, strB, true, CultureInfo.CurrentCulture);
+                       case StringComparison.InvariantCulture:
+                               return Compare (strA, strB, false, CultureInfo.InvariantCulture);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return Compare (strA, strB, true, CultureInfo.InvariantCulture);
+                       case StringComparison.Ordinal:
+                               return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
+                       default:
+                               string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
+                               throw new ArgumentException ("comparisonType", msg);
+                       }
+               }
 
+               public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
+               {
+                       switch (comparisonType) {
+                       case StringComparison.CurrentCulture:
+                               return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
+                       case StringComparison.InvariantCulture:
+                               return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
+                       case StringComparison.Ordinal:
+                               return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
+                       default:
+                               string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
+                               throw new ArgumentException ("comparisonType", msg);
+                       }
+               }
+
+               public static bool Equals (string a, string b, StringComparison comparisonType)
+               {
+                       return String.Compare (a, b, comparisonType) == 0;
+               }
+
+               public bool Equals (string value, StringComparison comparisonType)
+               {
+                       return String.Equals (this, value, comparisonType);
+               }
+#endif
                public int CompareTo (Object value)
                {
                        if (value == null)
@@ -371,15 +564,38 @@ namespace System
                        return Compare (this, strB, false);
                }
 
-               public static int CompareOrdinal (String strA, String strB)
+               public static unsafe int CompareOrdinal (String strA, String strB)
                {
                        if (strA == null) {
                                if (strB == null)
                                        return 0;
                                else
                                        return -1;
+                       } else if (strB == null) {
+                               return 1;
                        }
-                       else if (strB == null) {
+                       fixed (char* aptr = strA, bptr = strB) {
+                               char* ap = aptr;
+                               char* end = ap + Math.Min (strA.Length, strB.Length);
+                               char* bp = bptr;
+                               while (ap < end) {
+                                       if (*ap != *bp)
+                                               return *ap - *bp;
+                                       ap++;
+                                       bp++;
+                               }
+                               return strA.Length - strB.Length;
+                       }
+               }
+
+               internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
+               {
+                       if (strA == null) {
+                               if (strB == null)
+                                       return 0;
+                               else
+                                       return -1;
+                       } else if (strB == null) {
                                return 1;
                        }
 
@@ -387,10 +603,15 @@ namespace System
                         * instantiate (and chances are it already has
                         * been.)
                         */
-                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
+                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
                }
 
                public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
+               {
+                       return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
+               }
+
+               internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
                {
                        if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
                                throw new ArgumentOutOfRangeException ();
@@ -421,27 +642,32 @@ namespace System
                                len2 = strB.Length - indexB;
                        }
 
-                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
+                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
                }
 
                public bool EndsWith (String value)
                {
-                       if (value == null)
-                               throw new ArgumentNullException ("value");
-
-                       if (value.Length == 0)
-                               return true;
-
-                       if (value.length > this.length)
-                               return false;
+                       return EndsWith (value, false, CultureInfo.CurrentCulture);
+               }
 
-                       return (0 == Compare (this, length - value.length, value, 0, value.length));
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
+               {
+                       return (culture.CompareInfo.IsSuffix (this, value,
+                               ignoreCase ? CompareOptions.IgnoreCase :
+                               CompareOptions.None));
                }
 
                public int IndexOfAny (char [] anyOf)
                {
                        if (anyOf == null)
                                throw new ArgumentNullException ("anyOf");
+                       if (this.length == 0)
+                               return -1;
 
                        return InternalIndexOfAny (anyOf, 0, this.length);
                }
@@ -450,8 +676,8 @@ namespace System
                {
                        if (anyOf == null)
                                throw new ArgumentNullException ("anyOf");
-                       if (startIndex < 0 || startIndex >= this.length)
-                               throw new ArgumentOutOfRangeException ("sourceIndex");
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ("startIndex");
 
                        return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
                }
@@ -471,9 +697,123 @@ namespace System
                        return InternalIndexOfAny (anyOf, startIndex, count);
                }
 
+               unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
+               {
+                       if (anyOf.Length == 0)
+                               return -1;
+
+                       if (anyOf.Length == 1)
+                               return IndexOfImpl(anyOf[0], startIndex, count);
+
+                       fixed (char* any = anyOf) {
+                               int highest = *any;
+                               int lowest = *any;
+
+                               char* end_any_ptr = any + anyOf.Length;
+                               char* any_ptr = any;
+                               while (++any_ptr != end_any_ptr) {
+                                       if (*any_ptr > highest) {
+                                               highest = *any_ptr;
+                                               continue;
+                                       }
+
+                                       if (*any_ptr < lowest)
+                                               lowest = *any_ptr;
+                               }
+
+                               fixed (char* start = &start_char) {
+                                       char* ptr = start + startIndex;
+                                       char* end_ptr = ptr + count;
+
+                                       while (ptr != end_ptr) {
+                                               if (*ptr > highest || *ptr < lowest) {
+                                                       ptr++;
+                                                       continue;
+                                               }
+
+                                               if (*ptr == *any)
+                                                       return (int)(ptr - start);
+
+                                               any_ptr = any;
+                                               while (++any_ptr != end_any_ptr) {
+                                                       if (*ptr == *any_ptr)
+                                                               return (int)(ptr - start);
+                                               }
+
+                                               ptr++;
+                                       }
+                               }
+                       }
+                       return -1;
+               }
+
+
+#if NET_2_0
+               public int IndexOf (string value, StringComparison comparison)
+               {
+                       return IndexOf (value, 0, this.Length, comparison);
+               }
+
+               public int IndexOf (string value, int startIndex, StringComparison comparison)
+               {
+                       return IndexOf (value, startIndex, this.Length - startIndex, comparison);
+               }
+
+               public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
+               {
+                       switch (comparison) {
+                       case StringComparison.CurrentCulture:
+                               return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+                       case StringComparison.InvariantCulture:
+                               return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+                       case StringComparison.Ordinal:
+                               return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
+                       }
+                       throw new SystemException ("INTERNAL ERROR: should not reach here ...");
+               }
+
+               public int LastIndexOf (string value, StringComparison comparison)
+               {
+                       return LastIndexOf (value, value.Length - 1, value.Length, comparison);
+               }
+
+               public int LastIndexOf (string value, int startIndex, StringComparison comparison)
+               {
+                       return LastIndexOf (value, startIndex, startIndex + 1, comparison);
+               }
+
+               public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
+               {
+                       switch (comparison) {
+                       case StringComparison.CurrentCulture:
+                               return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+                       case StringComparison.InvariantCulture:
+                               return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+                       case StringComparison.Ordinal:
+                               return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
+                       }
+                       throw new SystemException ("INTERNAL ERROR: should not reach here ...");
+               }
+#endif
+
                public int IndexOf (char value)
                {
-                       return IndexOf (value, 0, this.length);
+                       if (this.length == 0)
+                               return -1;
+
+                       return IndexOfImpl (value, 0, this.length);
                }
 
                public int IndexOf (String value)
@@ -505,11 +845,48 @@ namespace System
                        if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
                                return -1;
 
-                       for (int pos = startIndex; pos < startIndex + count; pos++) {
-                               if (this[pos] == value)
-                                       return(pos);
+                       return IndexOfImpl (value, startIndex, count);
+               }
+
+               unsafe int IndexOfImpl (char value, int startIndex, int count)
+               {
+                       // It helps JIT compiler to optimize comparison
+                       int value_32 = (int)value;
+
+                       fixed (char* start = &start_char) {
+                               char* ptr = start + startIndex;
+                               char* end_ptr = ptr + (count >> 3 << 3);
+
+                               while (ptr != end_ptr) {
+                                       if (*ptr == value_32)
+                                               return (int)(ptr - start);
+                                       if (ptr[1] == value_32)
+                                               return (int)(ptr - start + 1);
+                                       if (ptr[2] == value_32)
+                                               return (int)(ptr - start + 2);
+                                       if (ptr[3] == value_32)
+                                               return (int)(ptr - start + 3);
+                                       if (ptr[4] == value_32)
+                                               return (int)(ptr - start + 4);
+                                       if (ptr[5] == value_32)
+                                               return (int)(ptr - start + 5);
+                                       if (ptr[6] == value_32)
+                                               return (int)(ptr - start + 6);
+                                       if (ptr[7] == value_32)
+                                               return (int)(ptr - start + 7);
+
+                                       ptr += 8;
+                               }
+
+                               end_ptr += count & 0x07;
+                               while (ptr != end_ptr) {
+                                       if (*ptr == value_32)
+                                               return (int)(ptr - start);
+
+                                       ptr++;
+                               }
+                               return -1;
                        }
-                       return -1;
                }
 
                /* But this one is culture-sensitive */
@@ -550,7 +927,7 @@ namespace System
                        if (anyOf == null) 
                                throw new ArgumentNullException ("anyOf");
 
-                       if (startIndex < 0 || startIndex > this.length)
+                       if (startIndex < 0 || startIndex >= this.length)
                                throw new ArgumentOutOfRangeException ();
 
                        if (this.length == 0)
@@ -564,7 +941,7 @@ namespace System
                        if (anyOf == null) 
                                throw new ArgumentNullException ("anyOf");
 
-                       if ((startIndex < 0) || (startIndex > this.Length))
+                       if ((startIndex < 0) || (startIndex >= this.Length))
                                throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
                        if ((count < 0) || (count > this.Length))
                                throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
@@ -581,8 +958,8 @@ namespace System
                {
                        if (this.length == 0)
                                return -1;
-                       else
-                               return LastIndexOf (value, this.length - 1, this.length);
+                       
+                       return LastIndexOfImpl (value, this.length - 1, this.length);
                }
 
                public int LastIndexOf (String value)
@@ -623,11 +1000,49 @@ namespace System
                        if (startIndex - count + 1 < 0)
                                throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
 
-                       for(int pos = startIndex; pos > startIndex - count; pos--) {
-                               if (this [pos] == value)
-                                       return pos;
+                       return LastIndexOfImpl (value, startIndex, count);
+               }
+
+               /* This method is culture-insensitive */
+               unsafe int LastIndexOfImpl (char value, int startIndex, int count)
+               {
+                       // It helps JIT compiler to optimize comparison
+                       int value_32 = (int)value;
+
+                       fixed (char* start = &start_char) {
+                               char* ptr = start + startIndex;
+                               char* end_ptr = ptr - (count >> 3 << 3);
+
+                               while (ptr != end_ptr) {
+                                       if (*ptr == value_32)
+                                               return (int)(ptr - start);
+                                       if (ptr[-1] == value_32)
+                                               return (int)(ptr - start) - 1;
+                                       if (ptr[-2] == value_32)
+                                               return (int)(ptr - start) - 2;
+                                       if (ptr[-3] == value_32)
+                                               return (int)(ptr - start) - 3;
+                                       if (ptr[-4] == value_32)
+                                               return (int)(ptr - start) - 4;
+                                       if (ptr[-5] == value_32)
+                                               return (int)(ptr - start) - 5;
+                                       if (ptr[-6] == value_32)
+                                               return (int)(ptr - start) - 6;
+                                       if (ptr[-7] == value_32)
+                                               return (int)(ptr - start) - 7;
+
+                                       ptr -= 8;
+                               }
+
+                               end_ptr -= count & 0x07;
+                               while (ptr != end_ptr) {
+                                       if (*ptr == value_32)
+                                               return (int)(ptr - start);
+
+                                       ptr--;
+                               }
+                               return -1;
                        }
-                       return -1;
                }
 
                /* But this one is culture-sensitive */
@@ -644,7 +1059,7 @@ namespace System
                                throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
 
                        if (value.Length == 0)
-                               return 0;
+                               return startIndex;
 
                        if (startIndex == 0 && this.length == 0)
                                return -1;
@@ -653,9 +1068,6 @@ namespace System
                        if (this.length == 0 && value.length > 0)
                                return -1;
 
-                       if (value.length > startIndex)
-                               return -1;
-
                        if (count == 0)
                                return -1;
 
@@ -670,28 +1082,47 @@ namespace System
                        return IndexOf (value) != -1;
                }
 
-               public bool IsNormalized ()
+               public static bool IsNullOrEmpty (String value)
                {
-                       return IsNormalized (NormalizationForm.FormC);
+                       return (value == null) || (value.Length == 0);
                }
 
-               public bool IsNormalized (NormalizationForm f)
+               public string Normalize ()
                {
-                       switch (f) {
-                       case NormalizationForm.FormKD:
-                               return Normalization.IsNormalized (this, 3);
-                       case NormalizationForm.FormKC:
-                               return Normalization.IsNormalized (this, 2);
-                       case NormalizationForm.FormD:
-                               return Normalization.IsNormalized (this, 1);
+                       return Normalize (NormalizationForm.FormC);
+               }
+
+               public string Normalize (NormalizationForm form)
+               {
+                       switch (form) {
                        default:
-                               return Normalization.IsNormalized (this, 0);
+                               return Normalization.Normalize (this, 0);
+                       case NormalizationForm.FormD:
+                               return Normalization.Normalize (this, 1);
+                       case NormalizationForm.FormKC:
+                               return Normalization.Normalize (this, 2);
+                       case NormalizationForm.FormKD:
+                               return Normalization.Normalize (this, 3);
                        }
                }
 
-               public static bool IsNullOrEmpty (String value)
+               public bool IsNormalized ()
                {
-                       return (value == null) || (value.Length == 0);
+                       return IsNormalized (NormalizationForm.FormC);
+               }
+
+               public bool IsNormalized (NormalizationForm form)
+               {
+                       switch (form) {
+                       default:
+                               return Normalization.IsNormalized (this, 0);
+                       case NormalizationForm.FormD:
+                               return Normalization.IsNormalized (this, 1);
+                       case NormalizationForm.FormKC:
+                               return Normalization.IsNormalized (this, 2);
+                       case NormalizationForm.FormKD:
+                               return Normalization.IsNormalized (this, 3);
+                       }
                }
 
                public string Remove (int startIndex)
@@ -703,25 +1134,6 @@ namespace System
 
                        return Remove (startIndex, this.length - startIndex);
                }
-
-               public string Normalize ()
-               {
-                       return Normalize (NormalizationForm.FormC);
-               }
-
-               public string Normalize (NormalizationForm f)
-               {
-                       switch (f) {
-                       case NormalizationForm.FormKD:
-                               return Normalization.Normalize (this, 3);
-                       case NormalizationForm.FormKC:
-                               return Normalization.Normalize (this, 2);
-                       case NormalizationForm.FormD:
-                               return Normalization.Normalize (this, 1);
-                       default:
-                               return Normalization.Normalize (this, 0);
-                       }
-               }
 #endif
 
                public String PadLeft (int totalWidth)
@@ -758,22 +1170,102 @@ namespace System
 
                public bool StartsWith (String value)
                {
-                       if (value == null)
-                               throw new ArgumentNullException ("value");
-                       
-                       if (value.Length == 0)
-                               return true;
+                       return StartsWith (value, false, CultureInfo.CurrentCulture);
+               }
 
-                       if (this.length < value.length)
+#if NET_2_0
+               [ComVisible (false)]
+               public bool StartsWith (string value, StringComparison comparisonType)
+               {
+                       switch (comparisonType) {
+                       case StringComparison.CurrentCulture:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
+                       case StringComparison.InvariantCulture:
+                               return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
+                       case StringComparison.Ordinal:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
+                       default:
                                return false;
+                       }
+               }
 
-                       return (0 == Compare (this, 0, value, 0 , value.length));
+               [ComVisible (false)]
+               public bool EndsWith (string value, StringComparison comparisonType)
+               {
+                       switch (comparisonType) {
+                       case StringComparison.CurrentCulture:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
+                       case StringComparison.CurrentCultureIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
+                       case StringComparison.InvariantCulture:
+                               return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
+                       case StringComparison.InvariantCultureIgnoreCase:
+                               return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
+                       case StringComparison.Ordinal:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
+                       case StringComparison.OrdinalIgnoreCase:
+                               return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
+                       default:
+                               return false;
+                       }
+               }
+
+#endif
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
+               {
+                       if (culture == null)
+                               culture = CultureInfo.CurrentCulture;
+                       
+                       return (culture.CompareInfo.IsPrefix (this, value,
+                               ignoreCase ? CompareOptions.IgnoreCase :
+                               CompareOptions.None));
                }
 
                /* This method is culture insensitive */
-               public String Replace (char oldChar, char newChar)
+               public unsafe String Replace (char oldChar, char newChar)
                {
-                       return InternalReplace (oldChar, newChar);
+                       if (this.length == 0 || oldChar == newChar)
+                               return this;
+
+                       int start_pos = IndexOfImpl (oldChar, 0, this.length);
+                       if (start_pos == -1)
+                               return this;
+
+                       if (start_pos < 4)
+                               start_pos = 0;
+
+                       string tmp = InternalAllocateStr(length);
+                       fixed (char* dest = tmp, src = &start_char) {
+                               if (start_pos != 0)
+                                       memcpy((byte*)dest, (byte*)src, start_pos * 2);
+
+                               char* end_ptr = dest + length;
+                               char* dest_ptr = dest + start_pos;
+                               char* src_ptr = src + start_pos;
+
+                               while (dest_ptr != end_ptr) {
+                                       if (*src_ptr == oldChar)
+                                               *dest_ptr = newChar;
+                                       else
+                                               *dest_ptr = *src_ptr;
+
+                                       ++src_ptr;
+                                       ++dest_ptr;
+                               }
+                       }
+                       return tmp;
                }
 
                /* This method is culture sensitive */
@@ -832,7 +1324,11 @@ namespace System
                        return culture.TextInfo.ToLower (this);
                }
 
+#if NET_2_0
+               public unsafe String ToLowerInvariant ()
+#else
                internal unsafe String ToLowerInvariant ()
+#endif
                {
                        string tmp = InternalAllocateStr (length);
                        fixed (char* source = &start_char, dest = tmp) {
@@ -865,7 +1361,11 @@ namespace System
                        return culture.TextInfo.ToUpper (this);
                }
 
+#if NET_2_0
+               public unsafe String ToUpperInvariant ()
+#else
                internal unsafe String ToUpperInvariant ()
+#endif
                {
                        string tmp = InternalAllocateStr (length);
                        fixed (char* source = &start_char, dest = tmp) {
@@ -892,11 +1392,6 @@ namespace System
                        return this;
                }
 
-               public String Trim ()
-               {
-                       return Trim (null);
-               }
-
                public static String Format (String format, Object arg0)
                {
                        return Format (null, format, new Object[] {arg0});
@@ -959,8 +1454,14 @@ namespace System
                                        object arg = args[n];
 
                                        string str;
+                                       ICustomFormatter formatter = null;
+                                       if (provider != null)
+                                               formatter = provider.GetFormat (typeof (ICustomFormatter))
+                                                       as ICustomFormatter;
                                        if (arg == null)
-                                               str = "";
+                                               str = String.Empty;
+                                       else if (formatter != null)
+                                               str = formatter.Format (arg_format, arg, provider);
                                        else if (arg is IFormattable)
                                                str = ((IFormattable)arg).ToString (arg_format, provider);
                                        else
@@ -969,14 +1470,15 @@ namespace System
                                        // pad formatted string and append to result
 
                                        if (width > str.length) {
-                                               string pad = new String (' ', width - str.length);
+                                               const char padchar = ' ';
+                                               int padlen = width - str.length;
 
                                                if (left_align) {
                                                        result.Append (str);
-                                                       result.Append (pad);
+                                                       result.Append (padchar, padlen);
                                                }
                                                else {
-                                                       result.Append (pad);
+                                                       result.Append (padchar, padlen);
                                                        result.Append (str);
                                                }
                                        }
@@ -995,7 +1497,7 @@ namespace System
                        }
 
                        if (start < format.length)
-                               result.Append (format.Substring (start));
+                               result.Append (format, start, format.Length - start);
                }
 
                public unsafe static String Copy (String str)
@@ -1115,13 +1617,13 @@ namespace System
 
                public unsafe static String Concat (String s1, String s2)
                {
-                       if (s1 == null) {
-                               if (s2 == null)
+                       if (s1 == null || s1.Length == 0) {
+                               if (s2 == null || s2.Length == 0)
                                        return String.Empty;
                                return s2;
                        }
 
-                       if (s2 == null)
+                       if (s2 == null || s2.Length == 0)
                                return s1; 
 
                        String tmp = InternalAllocateStr (s1.length + s2.length);
@@ -1142,24 +1644,24 @@ namespace System
 
                public unsafe static String Concat (String s1, String s2, String s3)
                {
-                       if (s1 == null){
-                               if (s2 == null){
-                                       if (s3 == null)
+                       if (s1 == null || s1.Length == 0){
+                               if (s2 == null || s2.Length == 0){
+                                       if (s3 == null || s3.Length == 0)
                                                return String.Empty;
                                        return s3;
                                } else {
-                                       if (s3 == null)
+                                       if (s3 == null || s3.Length == 0)
                                                return s2;
                                }
                                s1 = String.Empty;
                        } else {
-                               if (s2 == null){
-                                       if (s3 == null)
+                               if (s2 == null || s2.Length == 0){
+                                       if (s3 == null || s3.Length == 0)
                                                return s1;
                                        else
                                                s2 = String.Empty;
                                } else {
-                                       if (s3 == null)
+                                       if (s3 == null || s3.Length == 0)
                                                s3 = String.Empty;
                                }
                        }
@@ -1387,11 +1889,6 @@ namespace System
                        return Convert.ToSingle (this, provider);
                }
 
-               string IConvertible.ToString (IFormatProvider format)
-               {
-                       return this;
-               }
-
                object IConvertible.ToType (Type conversionType, IFormatProvider provider)
                {
                        return Convert.ToType (this, conversionType,  provider);
@@ -1412,11 +1909,6 @@ namespace System
                        return Convert.ToUInt64 (this, provider);
                }
 
-               TypeCode IConvertible.GetTypeCode ()
-               {
-                       return TypeCode.String;
-               }
-
                public int Length {
                        get {
                                return length;
@@ -1428,6 +1920,13 @@ namespace System
                        return new CharEnumerator (this);
                }
 
+#if NET_2_0
+               IEnumerator<char> IEnumerable<char>.GetEnumerator ()
+               {
+                       return GetEnumerator ();
+               }
+#endif
+
                IEnumerator IEnumerable.GetEnumerator ()
                {
                        return new CharEnumerator (this);
@@ -1452,9 +1951,10 @@ namespace System
 
                                if (str[ptr] == ',') {
                                        // White space between ',' and number or sign.
-                                       int start = ++ptr;
+                                       ++ptr;
                                        while (Char.IsWhiteSpace (str [ptr]))
                                                ++ptr;
+                                       int start = ptr;
 
                                        format = str.Substring (start, ptr - start);
 
@@ -1469,7 +1969,7 @@ namespace System
                                else {
                                        width = 0;
                                        left_align = false;
-                                       format = "";
+                                       format = String.Empty;
                                }
 
                                // F = argument format (string)
@@ -1528,14 +2028,24 @@ namespace System
                        if (newLength > length)
                                throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
 
-                       length = newLength;
-
                        // zero terminate, we can pass string objects directly via pinvoke
+                       // we also zero the rest of the string, since the new GC needs to be
+                       // able to handle the changing size (it will skip the 0 bytes).
                        fixed (char * pStr = &start_char) {
-                               pStr [length] = '\0';
+                               char *p = pStr + newLength;
+                               char *end = pStr + length;
+                               while (p < end) {
+                                       p [0] = '\0';
+                                       p++;
+                               }
                        }
+                       length = newLength;
                }
 
+#if NET_2_0
+               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+               // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
                public unsafe override int GetHashCode ()
                {
                        fixed (char * c = this) {
@@ -1553,6 +2063,196 @@ namespace System
                        }
                }
 
+               internal unsafe int GetCaseInsensitiveHashCode ()
+               {
+                       TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
+                       fixed (char * c = this) {
+                               char * cc = c;
+                               char * end = cc + length - 1;
+                               int h = 0;
+                               for (;cc < end; cc += 2) {
+                                       h = (h << 5) - h + ti.ToUpper (*cc);
+                                       h = (h << 5) - h + ti.ToUpper (cc [1]);
+                               }
+                               ++end;
+                               if (cc < end)
+                                       h = (h << 5) - h + ti.ToUpper (*cc);
+                               return h;
+                       }
+               }
+
+               // Certain constructors are redirected to CreateString methods with
+               // matching argument list. The this pointer should not be used.
+
+               private unsafe String CreateString (sbyte* value)
+               {
+                       if (value == null)
+                               return String.Empty;
+
+                       byte* bytes = (byte*) value;
+                       int length = 0;
+
+                       try {
+                               while (bytes++ [0] != 0)
+                                       length++;
+                       } catch (NullReferenceException) {
+                               throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
+#if NET_2_0
+                       } catch (AccessViolationException) {
+                               throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
+#endif
+                       }
+
+                       return CreateString (value, 0, length, null);
+               }
+
+               private unsafe String CreateString (sbyte* value, int startIndex, int length)
+               {
+                       return CreateString (value, startIndex, length, null);
+               }
+
+               private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
+               {
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
+                       if (value + startIndex < value)
+                               throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
+
+                       bool isDefaultEncoding;
+
+                       if (isDefaultEncoding = (enc == null)) {
+#if NET_2_0
+                               if (value == null)
+                                       throw new ArgumentNullException ("value");
+                               if (length == 0)
+#else
+                               if (value == null || length == 0)
+#endif
+                                       return String.Empty;
+
+                               enc = Encoding.Default;
+                       }
+
+                       byte [] bytes = new byte [length];
+
+                       if (length != 0)
+                               fixed (byte* bytePtr = bytes)
+                                       try {
+                                               memcpy (bytePtr, (byte*) (value + startIndex), length);
+                                       } catch (NullReferenceException) {
+#if !NET_2_0
+                                               if (!isDefaultEncoding)
+                                                       throw;
+#endif
+
+                                               throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
+#if NET_2_0
+                                       } catch (AccessViolationException) {
+                                               if (!isDefaultEncoding)
+                                                       throw;
+
+                                               throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
+#endif
+                                       }
+
+                       // GetString () is called even when length == 0
+                       return enc.GetString (bytes);
+               }
+
+               unsafe string CreateString (char *value)
+               {
+                       if (value == null)
+                               return string.Empty;
+                       char *p = value;
+                       int i = 0;
+                       while (*p != 0) {
+                               ++i;
+                               ++p;
+                       }
+                       string result = InternalAllocateStr (i);
+
+                       if (i != 0) {
+                               fixed (char *dest = result) {
+                                       memcpy ((byte*)dest, (byte*)value, i * 2);
+                               }
+                       }
+                       return result;
+               }
+
+               unsafe string CreateString (char *value, int startIndex, int length)
+               {
+                       if (length == 0)
+                               return string.Empty;
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex");
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length");
+
+                       string result = InternalAllocateStr (length);
+
+                       fixed (char *dest = result) {
+                               memcpy ((byte*)dest, (byte*)(value + startIndex), length * 2);
+                       }
+                       return result;
+               }
+
+               unsafe string CreateString (char [] val, int startIndex, int length)
+               {
+                       if (val == null)
+                               throw new ArgumentNullException ("val");
+                       if (startIndex < 0)
+                               throw new ArgumentOutOfRangeException ("startIndex");
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length");
+                       if (startIndex > val.Length - length)
+                               throw new ArgumentOutOfRangeException ("Out of range");
+                       if (length == 0)
+                               return string.Empty;
+
+                       string result = InternalAllocateStr (length);
+
+                       fixed (char *dest = result, src = val) {
+                               memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
+                       }
+                       return result;
+               }
+
+               unsafe string CreateString (char [] val)
+               {
+                       if (val == null)
+                               return string.Empty;
+                       if (val.Length == 0)
+                               return string.Empty;
+                       string result = InternalAllocateStr (val.Length);
+
+                       fixed (char *dest = result, src = val) {
+                               memcpy ((byte*)dest, (byte*)src, val.Length * 2);
+                       }
+                       return result;
+               }
+
+               unsafe string CreateString (char c, int count)
+               {
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count");
+                       if (count == 0)
+                               return string.Empty;
+                       string result = InternalAllocateStr (count);
+                       fixed (char *dest = result) {
+                               char *p = dest;
+                               char *end = p + count;
+                               while (p < end) {
+                                       *p = c;
+                                       p++;
+                               }
+                       }
+                       return result;
+               }
+
                /* helpers used by the runtime as well as above or eslewhere in corlib */
                internal static unsafe void memset (byte *dest, int val, int len)
                {
@@ -1600,7 +2300,7 @@ namespace System
                        }
                }
 
-               internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
+               static unsafe void memcpy4 (byte *dest, byte *src, int size) {
                        /*while (size >= 32) {
                                // using long is better than int and slower than double
                                // FIXME: enable this only on correct alignment or on platforms
@@ -1678,7 +2378,8 @@ namespace System
                        if (size > 0)
                                ((byte*)dest) [0] = ((byte*)src) [0];
                }
-               static unsafe void memcpy (byte *dest, byte *src, int size) {
+
+               internal static unsafe void memcpy (byte *dest, byte *src, int size) {
                        // FIXME: if pointers are not aligned, try to align them
                        // so a faster routine can be used. Handle the case where
                        // the pointers can't be reduced to have the same alignment
@@ -1735,9 +2436,6 @@ namespace System
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String InternalReplace (char oldChar, char newChar);
-
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
 
@@ -1745,14 +2443,11 @@ namespace System
                private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern String[] InternalSplit (char[] separator, int count);
+               private extern String[] InternalSplit (char[] separator, int count, int options);
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern String InternalTrim (char[] chars, int typ);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
-
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);