2004-04-02 Dick Porter <dick@ximian.com>
[mono.git] / mcs / class / corlib / System / String.cs
index decf3c715caa3a7b5e3d7486ad8a837cf9b11e8f..6d92bf96d80c38354604421e4c379c89efe85963 100644 (file)
-// -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 //
 // System.String.cs
 //
-// Author:
+// Authors:
+//   Patrik Torstensson
 //   Jeffrey Stedfast (fejj@ximian.com)
+//   Dan Lewis (dihlewis@yahoo.co.uk)
 //
 // (C) 2001 Ximian, Inc.  http://www.ximian.com
 //
 
-// FIXME: from what I gather from msdn, when a function is to return an empty string
-//        we should be returning this.Empty - some methods do this and others don't.
-
-// FIXME: I didn't realise until later that `string' has a .Length method and so
-//        I am missing some proper bounds-checking in some methods. Find these
-//        instances and throw the ArgumentOutOfBoundsException at the programmer.
-//        I like pelting programmers with ArgumentOutOfBoundsException's :-)
-
-// FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
-//        need to be made unicode aware.
-
-// FIXME: when you have a char carr[], does carr.Length include the terminating null char?
-
 using System;
 using System.Text;
 using System.Collections;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 
-namespace System {
+namespace System
+{
+       [Serializable]
+       public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable
+       {
+               [NonSerialized] private int length;
+               [NonSerialized] private char start_char;
+
+               private const int COMPARE_CASE = 0;
+               private const int COMPARE_INCASE = 1;
+               private const int COMPARE_ORDINAL = 2;
 
-       public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
-               public static readonly string Empty = "";
-               private char[] c_str;
-               private int length;
+               public static readonly String Empty = "";
 
-               // Constructors
-               unsafe public String (char *value)
+               public static unsafe bool Equals (string a, string b)
                {
-                       int i;
+                       if ((a as object) == (b as object))
+                               return true;
 
-                       // FIXME: can I do value.Length here?
-                       if (value == null) {
-                               this.length = 0;
-                       } else {
-                               for (i = 0; *(value + i) != '\0'; i++);
-                               this.length = i;
-                       }
+                       if (a == null || b == null)
+                               return false;
+
+                       int len = a.length;
+
+                       if (len != b.length)
+                               return false;
+
+                       if (len == 0)
+                               return true;
 
-                       this.c_str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               this.c_str[i] = *(value + i);
-                       this.c_str[i] = '\0';
+                       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;
+
+                               // check by twos
+                               int * sint1 = (int *) s1, sint2 = (int *) s2;
+                               int n2 = len >> 1;
+                               do {
+                                       if (*sint1++ != *sint2++)
+                                               return false;
+                               } while (--n2 != 0);
+
+                               // nothing left
+                               if ((len & 1) == 0)
+                                       return true;
+
+                               // check the last one
+                               return *(char *) sint1 == *(char *) sint2;
+                       }
                }
 
-               public String (char[] value)
+               public static bool operator == (String a, String b)
                {
-                       int i;
+                       return Equals (a, b);
+               }
 
-                       // FIXME: value.Length includes the terminating null char?
-                       this.length = value != null ? strlen (value): 0;
-                       this.c_str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               this.c_str[i] = value[i];
-                       this.c_str[i] = '\0';
+               public static bool operator != (String a, String b)
+               {
+                       return !Equals (a, b);
                }
 
-               unsafe public String (sbyte *value)
+               public override bool Equals (Object obj)
                {
-                       // FIXME: consider unicode?
-                       int i;
+                       return Equals (this, obj as String);
+               }
 
-                       // FIXME: can I do value.Length here? */
-                       if (value == null) {
-                               this.length = 0;
-                       } else {
-                               for (i = 0; *(value + i) != '\0'; i++);
-                               this.length = i;
-                       }
+               public bool Equals (String value)
+               {
+                       return Equals (this, value);
+               }
 
-                       this.c_str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               this.c_str[i] = (char) *(value + i);
-                       this.c_str[i] = '\0';
+               [IndexerName ("Chars")]
+               public extern char this [int index] {
+                       [MethodImplAttribute (MethodImplOptions.InternalCall)]
+                       get;
                }
 
-               public String (char c, int count)
+               public Object Clone ()
                {
-                       int i;
+                       return this;
+               }
 
-                       this.length = count;
-                       this.c_str = new char [count + 1];
-                       for (i = 0; i < count; i++)
-                               this.c_str[i] = c;
-                       this.c_str[i] = '\0';
+               public TypeCode GetTypeCode ()
+               {
+                       return TypeCode.String;
                }
 
-               unsafe public String (char *value, int startIndex, int length)
+               public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
                {
-                       int i;
+                       // LAMESPEC: should I null-terminate?
+                       if (destination == null)
+                               throw new ArgumentNullException ("destination");
 
-                       if (value == null && startIndex != 0 && length != 0)
-                               throw new ArgumentNullException ();
+                       if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException (); 
+
+                       if (sourceIndex + count > Length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       if (startIndex < 0 || length < 0)
+                       if (destinationIndex + count > destination.Length)
                                throw new ArgumentOutOfRangeException ();
 
-                       this.length = length;
-                       this.c_str = new char [length + 1];
-                       for (i = 0; i < length; i++)
-                               this.c_str[i] = *(value + startIndex + i);
-                       this.c_str[i] = '\0';
+                       InternalCopyTo (sourceIndex, destination, destinationIndex, count);
                }
 
-               public String (char[] value, int startIndex, int length)
+               public char[] ToCharArray ()
                {
-                       int i;
-
-                       if (value == null && startIndex != 0 && length != 0)
-                               throw new ArgumentNullException ();
-
-                       if (startIndex < 0 || length < 0)
-                               throw new ArgumentOutOfRangeException ();
-
-                       this.length = length;
-                       this.c_str = new char [length + 1];
-                       for (i = 0; i < length; i++)
-                               this.c_str[i] = value[startIndex + i];
-                       this.c_str[i] = '\0';
+                       return ToCharArray (0, length);
                }
 
-               unsafe public String (sbyte *value, int startIndex, int length)
+               public char[] ToCharArray (int startIndex, int length)
                {
-                       // FIXME: consider unicode?
-                       int i;
+                       if (startIndex < 0 || length < 0 || startIndex + length > this.length)
+                               throw new ArgumentOutOfRangeException (); 
 
-                       if (value == null && startIndex != 0 && length != 0)
-                               throw new ArgumentNullException ();
+                       char [] tmp = new char[length];
 
-                       if (startIndex < 0 || length < 0)
-                               throw new ArgumentOutOfRangeException ();
+                       InternalCopyTo (startIndex, tmp, 0, length);
 
-                       this.length = length;
-                       this.c_str = new char [length + 1];
-                       for (i = 0; i < length; i++)
-                               this.c_str[i] = (char) *(value + startIndex + i);
-                       this.c_str[i] = '\0';
+                       return tmp;
                }
 
-               unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
+               public String [] Split (params char [] separator)
                {
-                       // FIXME: implement me
+                       return Split (separator, Int32.MaxValue);
                }
 
-               ~String ()
+               public String[] Split (char[] separator, int count)
                {
-                       // FIXME: is there anything we need to do here?
-                       /*base.Finalize ();*/
-               }
+                       if (separator == null || separator.Length == 0)
+                               separator = WhiteChars;
 
-               // Properties
-               public int Length {
-                       get {
-                               return this.length;
-                       }
-               }
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ();
 
-               // FIXME: is this correct syntax??
-               public char this [int index] {
-                       get {
-                               if (index > this.length)
-                                       throw new ArgumentOutOfRangeException ();
+                       if (count == 0) 
+                               return new String[0];
 
-                               return this.c_str[index];
-                       }
-               }
+                       if (count == 1) 
+                               return new String[1] { ToString() };
 
-               // Private helper methods
-               private static int strlen (char[] str)
-               {
-                       // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
-                       return str.Length;
+                       return InternalSplit (separator, count);
                }
 
-               private static char tolowerordinal (char c)
+               public String Substring (int startIndex)
                {
-                       // FIXME: implement me
-                       return c;
-               }
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ("startIndex");
 
-               private static bool is_lwsp (char c)
-               {
-                       /* this comes from the msdn docs for String.Trim() */
-                       if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
-                           (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
-                               return true;
-                       else
-                               return false;
+                       string tmp = InternalAllocateStr (this.length - startIndex);
+                       InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
+
+                       return tmp;
                }
 
-               private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
+               public String Substring (int startIndex, int length)
                {
-                       /* (hopefully) Unicode-safe Boyer-Moore implementation */
-                       int[] skiptable = new int[65536];  /* our unicode-safe skip-table */
-                       int h, n, he, ne, hc, nc, i;
+                       if (length < 0 || startIndex < 0 || startIndex + length > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       if (haystack == null || needle == null)
-                               throw new ArgumentNullException ();
+                       if (length == 0)
+                               return String.Empty;
 
-                       /* if the search buffer is shorter than the pattern buffer, we can't match */
-                       if (count < needle.Length)
-                               return -1;
+                       string tmp = InternalAllocateStr (length);
+                       InternalStrcpy (tmp, 0, this, startIndex, length);
 
-                       /* return an instant match if the pattern is 0-length */
-                       if (needle.Length == 0)
-                               return startIndex;
+                       return tmp;
+               }       
 
-                       /* set a pointer at the end of each string */
-                       ne = needle.Length - 1;      /* position of char before '\0' */
-                       he = startIndex + count;     /* position of last valid char */
-
-                       /* init the skip table with the pattern length */
-                       for (i = 0; i < 65536; i++)
-                               skiptable[i] = needle.Length;
-
-                       /* set the skip value for the chars that *do* appear in the
-                        * pattern buffer (needle) to the distance from the index to
-                        * the end of the pattern buffer. */
-                       for (nc = 0; nc < ne; nc++)
-                               skiptable[(int) needle[nc]] = ne - nc;
-
-                       h = startIndex;
-                       while (count >= needle.Length) {
-                               hc = h + needle.Length - 1;  /* set the haystack compare pointer */
-                               nc = ne;                     /* set the needle compare pointer */
-
-                               /* work our way backwards until they don't match */
-                               for (i = 0; nc > 0; nc--, hc--, i++)
-                                       if (needle[nc] != haystack[hc])
-                                               break;
-
-                               if (needle[nc] != haystack[hc]) {
-                                       n = skiptable[(int) haystack[hc]] - i;
-                                       h += n;
-                                       count -= n;
-                               } else
-                                       return h;
-                       }
+               private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
+                       (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
+                       (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
+                       (char) 0x3000, (char) 0xFEFF };
 
-                       return -1;
+               public String Trim (params char[] trimChars)
+               {
+                       if (trimChars == null || trimChars.Length == 0)
+                               trimChars = WhiteChars;
+
+                       return InternalTrim (trimChars, 0);
                }
 
-               // Methods
-               public object Clone ()
+               public String TrimStart (params char[] trimChars)
                {
-                       // FIXME: implement me
-                       return null;
+                       if (trimChars == null || trimChars.Length == 0)
+                               trimChars = WhiteChars;
+
+                       return InternalTrim (trimChars, 1);
                }
 
-               public static int Compare (string strA, string strB)
+               public String TrimEnd (params char[] trimChars)
                {
-                       int i;
+                       if (trimChars == null || trimChars.Length == 0)
+                               trimChars = WhiteChars;
 
-                       /* Does this remind anyone of the nautilus string.h wrappers? ;-) */
-                       if (strA == null) {
-                               if (strB == null)
-                                       return 0;
-                               else
-                                       return -1;
-                       } else if (strB == null)
-                               return 1;
-
-                       for (i = 0; strA[i] == strB[i] && strA[i] != '\0'; i++);
+                       return InternalTrim (trimChars, 2);
+               }
 
-                       return ((int) (strA[i] - strB[i]));
+               public static int Compare (String strA, String strB)
+               {
+                       return Compare (strA, strB, false, CultureInfo.CurrentCulture);
                }
 
-               public static int Compare (string strA, string strB, bool ignoreCase)
+               public static int Compare (String strA, String strB, bool ignoreCase)
                {
-                       int i;
+                       return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
+               }
 
-                       if (!ignoreCase)
-                               return Compare (strA, strB);
+               public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
+               {
+                       if (culture == null)
+                               throw new ArgumentNullException ("culture");
 
-                       /*
-                        * And here I thought Eazel developers were on crack...
-                        * if a string is null it should pelt the programmer with
-                        * ArgumentNullExceptions, damnit!
-                        */
                        if (strA == null) {
                                if (strB == null)
                                        return 0;
                                else
                                        return -1;
-                       } else if (strB == null)
-                               return 1;
 
-                       for (i = 0; strA[i] != '\0' && strB[i] != '\0'; i++) {
-                               if (Char.ToLower (strA[i]) != Char.ToLower (strB[i]))
-                                       break;
                        }
+                       else if (strB == null) {
+                               return 1;
+                       }
+
+                       CompareOptions compopts;
 
-                       return ((int) (strA[i] - strB[i]));
+                       if (ignoreCase)
+                               compopts = CompareOptions.IgnoreCase;
+                       else
+                               compopts = CompareOptions.None;
+
+                       return culture.CompareInfo.Compare (strA, strB, compopts);
                }
 
-               public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
+               public static int Compare (String strA, int indexA, String strB, int indexB, int length)
                {
-                       // FIXME: implement me
-                       return 0;
+                       return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
                }
 
-               public static int Compare (string strA, int indexA, string strB, int indexB, int length)
+               public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
                {
-                       int i;
-
-                       if (length < 0 || indexA < 0 || indexB < 0)
-                               throw new ArgumentOutOfRangeException ();
+                       return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
+               }
+               
+               public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
+               {
+                       if (culture == null)
+                               throw new ArgumentNullException ("culture");
 
-                       if (indexA > strA.Length || indexB > strB.Length)
+                       if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
                                throw new ArgumentOutOfRangeException ();
 
-                       /* And again with the ("" > null) logic... lord have mercy! */
+                       if (length == 0)
+                               return 0;
+                       
                        if (strA == null) {
-                               if (strB == null)
+                               if (strB == null) {
                                        return 0;
-                               else
+                               } else {
                                        return -1;
-                       } else if (strB == null)
+                               }
+                       }
+                       else if (strB == null) {
                                return 1;
-
-                       for (i = 0; i < length - 1; i++) {
-                               if (strA[indexA + i] != strB[indexB + i])
-                                       break;
                        }
 
-                       return ((int) (strA[indexA + i] - strB[indexB + i]));
-               }
-
-               public static int Compare (string strA, int indexA, string strB, int indexB,
-                                          int length, bool ignoreCase)
-               {
-                       int i;
-
-                       if (!ignoreCase)
-                               return Compare (strA, indexA, strB, indexB, length);
+                       CompareOptions compopts;
 
-                       if (length < 0 || indexA < 0 || indexB < 0)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (indexA > strA.Length || indexB > strB.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (ignoreCase)
+                               compopts = CompareOptions.IgnoreCase;
+                       else
+                               compopts = CompareOptions.None;
 
-                       /* When will the hurting stop!?!? */
-                       if (strA == null) {
-                               if (strB == null)
-                                       return 0;
-                               else
-                                       return -1;
-                       } else if (strB == null)
-                               return 1;
+                       /* Need to cap the requested length to the
+                        * length of the string, because
+                        * CompareInfo.Compare will insist that length
+                        * <= (string.Length - offset)
+                        */
+                       int len1 = length;
+                       int len2 = length;
+                       
+                       if (length > (strA.Length - indexA)) {
+                               len1 = strA.Length - indexA;
+                       }
 
-                       for (i = 0; i < length - 1; i++) {
-                               if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
-                                       break;
+                       if (length > (strB.Length - indexB)) {
+                               len2 = strB.Length - indexB;
                        }
 
-                       return ((int) (strA[indexA + i] - strB[indexB + i]));
+                       return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
                }
 
-               public static int Compare (string strA, int indexA, string strB, int indexB,
-                                          int length, bool ignoreCase, CultureInfo culture)
+               public int CompareTo (Object value)
                {
-                       if (culture == null)
-                               throw new ArgumentNullException ();
+                       if (value == null)
+                               return 1;
 
-                       if (length < 0 || indexA < 0 || indexB < 0)
-                               throw new ArgumentOutOfRangeException ();
+                       if (!(value is String))
+                               throw new ArgumentException ();
 
-                       if (indexA > strA.Length || indexB > strB.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       return String.Compare (this, (String) value, false);
+               }
 
-                       /* I can't take it anymore! */
-                       if (strA == null) {
-                               if (strB == null)
-                                       return 0;
-                               else
-                                       return -1;
-                       } else if (strB == null)
+               public int CompareTo (String strB)
+               {
+                       if (strB == null)
                                return 1;
-                       // FIXME: implement me
-                       return 0;
+
+                       return Compare (this, strB, false);
                }
 
-               public static int CompareOrdinal (string strA, string strB)
+               public static int CompareOrdinal (String strA, String strB)
                {
-                       int i;
-
-                       /* Please God, make it stop! */
                        if (strA == null) {
                                if (strB == null)
                                        return 0;
                                else
                                        return -1;
-                       } else if (strB == null)
+                       }
+                       else if (strB == null) {
                                return 1;
-
-                       for (i = 0; strA[i] != '\0'; i++) {
-                               char cA, cB;
-
-                               cA = tolowerordinal (strA[i]);
-                               cB = tolowerordinal (strB[i]);
-
-                               if (cA != cB)
-                                       break;
                        }
 
-                       return ((int) (strA[i] - strB[i]));
+                       /* Invariant, because that is cheaper to
+                        * instantiate (and chances are it already has
+                        * been.)
+                        */
+                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
                }
 
-               public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
-                                                 int length)
+               public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
                {
-                       int i;
-
-                       if (length < 0 || indexA < 0 || indexB < 0)
+                       if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
                                throw new ArgumentOutOfRangeException ();
 
-                       /* Nooooooooo!! */
                        if (strA == null) {
                                if (strB == null)
                                        return 0;
                                else
                                        return -1;
-                       } else if (strB == null)
+                       }
+                       else if (strB == null) {
                                return 1;
-
-                       for (i = 0; i < length; i++) {
-                               char cA, cB;
-
-                               cA = tolowerordinal (strA[indexA + i]);
-                               cB = tolowerordinal (strB[indexB + i]);
-
-                               if (cA != cB)
-                                       break;
                        }
 
-                       return ((int) (strA[indexA + i] - strB[indexB + i]));
-               }
+                       /* Need to cap the requested length to the
+                        * length of the string, because
+                        * CompareInfo.Compare will insist that length
+                        * <= (string.Length - offset)
+                        */
+                       int len1 = length;
+                       int len2 = length;
 
-               public int CompareTo (object obj)
-               {
-                       return Compare (this, obj == null ? null : obj.ToString ());
-               }
+                       if (length > (strA.Length - indexA)) {
+                               len1 = strA.Length - indexA;
+                       }
 
-               public int CompareTo (string str)
-               {
-                       return Compare (this, str);
-               }
+                       if (length > (strB.Length - indexB)) {
+                               len2 = strB.Length - indexB;
+                       }
 
-               public static string Concat (object arg)
-               {
-                       return arg != null ? arg.ToString () : String.Empty;
+                       return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
                }
 
-               public static string Concat (params object[] args)
+               public bool EndsWith (String value)
                {
-                       string[] strings;
-                       char[] str;
-                       int len, i;
-
-                       if (args == null)
-                               throw new ArgumentNullException ();
-
-                       strings = new string [args.Length];
-                       len = 0;
-                       i = 0;
-                       foreach (object arg in args) {
-                               /* use Empty for each null argument */
-                               if (arg == null)
-                                       strings[i] = String.Empty;
-                               else
-                                       strings[i] = arg.ToString ();
-                               len += strings[i].Length;
-                               i++;
-                       }
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
 
-                       if (len == 0)
-                               return String.Empty;
+                       if (value == String.Empty)
+                               return true;
 
-                       str = new char [len + 1];
-                       i = 0;
-                       for (int j = 0; j < strings.Length; j++)
-                               for (int k = 0; k < strings[j].Length; k++)
-                                       str[i++] = strings[j][k];
-                       str[i] = '\0';
+                       if (value.length > this.length)
+                               return false;
 
-                       return new String (str);
+                       return (0 == Compare (this, length - value.length, value, 0, value.length));
                }
 
-               public static string Concat (params string[] values)
+               public int IndexOfAny (char [] anyOf)
                {
-                       int len, i;
-                       char[] str;
-
-                       if (values == null)
-                               throw new ArgumentNullException ();
-
-                       len = 0;
-                       foreach (string value in values)
-                               len += value != null ? value.Length : 0;
-
-                       if (len == 0)
-                               return String.Empty;
+                       if (anyOf == null)
+                               throw new ArgumentNullException ("anyOf");
 
-                       str = new char [len + 1];
-                       i = 0;
-                       foreach (string value in values) {
-                               if (value == null)
-                                       continue;
-
-                               for (int j = 0; j < value.Length; j++)
-                                       str[i++] = value[j];
-                       }
-                       str[i] = '\0';
-
-                       return new String (str);
+                       return InternalIndexOfAny (anyOf, 0, this.length);
                }
 
-               public static string Concat (object arg0, object arg1)
+               public int IndexOfAny (char [] anyOf, int startIndex)
                {
-                       string str0 = arg0 != null ? arg0.ToString () : String.Empty;
-                       string str1 = arg1 != null ? arg1.ToString () : String.Empty;
+                       if (anyOf == null)
+                               throw new ArgumentNullException ("anyOf");
+                       if (startIndex < 0 || startIndex >= this.length)
+                               throw new ArgumentOutOfRangeException ("sourceIndex");
 
-                       return Concat (str0, str1);
+                       return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
                }
 
-               public static string Concat (string str0, string str1)
+               public int IndexOfAny (char [] anyOf, int startIndex, int count)
                {
-                       char[] concat;
-                       int i, j, len;
-
-                       if (str0 == null)
-                               str0 = String.Empty;
-                       if (str1 == null)
-                               str1 = String.Empty;
-
-                       len = str0.Length + str1.Length;
-                       if (len == 0)
-                               return String.Empty;
-
-                       concat = new char [len + 1];
-                       for (i = 0; i < str0.Length; i++)
-                               concat[i] = str0[i];
-                       for (j = 0 ; j < str1.Length; j++)
-                               concat[i + j] = str1[j];
-                       concat[len] = '\0';
+                       if (anyOf == null)
+                               throw new ArgumentNullException ("anyOf");
+                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       return new String (concat);
+                       return InternalIndexOfAny (anyOf, startIndex, count);
                }
 
-               public static string Concat (object arg0, object arg1, object arg2)
+               public int IndexOf (char value)
                {
-                       string str0 = arg0 != null ? arg0.ToString () : String.Empty;
-                       string str1 = arg1 != null ? arg1.ToString () : String.Empty;
-                       string str2 = arg2 != null ? arg2.ToString () : String.Empty;
-
-                       return Concat (str0, str1, str2);
+                       return IndexOf (value, 0, this.length);
                }
 
-               public static string Concat (string str0, string str1, string str2)
+               public int IndexOf (String value)
                {
-                       char[] concat;
-                       int i, j, k, len;
-
-                       if (str0 == null)
-                               str0 = String.Empty;
-                       if (str1 == null)
-                               str1 = String.Empty;
-                       if (str2 == null)
-                               str2 = String.Empty;
-
-                       len = str0.Length + str1.Length + str2.Length;
-                       if (len == 0)
-                               return String.Empty;
-
-                       concat = new char [len + 1];
-                       for (i = 0; i < str0.Length; i++)
-                               concat[i] = str0[i];
-                       for (j = 0; j < str1.Length; j++)
-                               concat[i + j] = str1[j];
-                       for (k = 0; k < str2.Length; k++)
-                               concat[i + j + k] = str2[k];
-                       concat[len] = '\0';
-
-                       return new String (concat);
+                       return IndexOf (value, 0, this.length);
                }
 
-               public static string Concat (string str0, string str1, string str2, string str3)
+               public int IndexOf (char value, int startIndex)
                {
-                       char[] concat;
-                       int i, j, k, l, len;
-
-                       if (str0 == null)
-                               str0 = String.Empty;
-                       if (str1 == null)
-                               str1 = String.Empty;
-                       if (str2 == null)
-                               str2 = String.Empty;
-                       if (str3 == null)
-                               str3 = String.Empty;
-
-                       len = str0.Length + str1.Length + str2.Length + str3.Length;
-                       if (len == 0)
-                               return String.Empty;
-
-                       concat = new char [len + 1];
-                       for (i = 0; i < str0.Length; i++)
-                               concat[i] = str0[i];
-                       for (j = 0; j < str1.Length; j++)
-                               concat[i + j] = str1[j];
-                       for (k = 0; k < str2.Length; k++)
-                               concat[i + j + k] = str2[k];
-                       for (l = 0; l < str3.Length; l++)
-                               concat[i + j + k + l] = str3[l];
-                       concat[len] = '\0';
-
-                       return new String (concat);
+                       return IndexOf (value, startIndex, this.length - startIndex);
                }
 
-               public static string Copy (string str)
+               public int IndexOf (String value, int startIndex)
                {
-                       // FIXME: how do I *copy* a string if I can only have 1 of each?
-                       if (str == null)
-                               throw new ArgumentNullException ();
-
-                       return str;
+                       return IndexOf (value, startIndex, this.length - startIndex);
                }
 
-               public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
+               /* This method is culture-insensitive */
+               public int IndexOf (char value, int startIndex, int count)
                {
-                       // LAMESPEC: should I null-terminate?
-                       int i;
-
-                       if (destination == null)
-                               throw new ArgumentNullException ();
-
-                       if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (sourceIndex + count > this.length)
+                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
                                throw new ArgumentOutOfRangeException ();
 
-                       if (destinationIndex + count > destination.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
+                               return -1;
 
-                       for (i = 0; i < count; i++)
-                               destination[destinationIndex + i] = this.c_str[sourceIndex + i];
+                       for (int pos = startIndex; pos < startIndex + count; pos++) {
+                               if (this[pos] == value)
+                                       return(pos);
+                       }
+                       return -1;
                }
 
-               public bool EndsWith (string value)
+               /* But this one is culture-sensitive */
+               public int IndexOf (String value, int startIndex, int count)
                {
-                       bool endswith = true;
-                       int start, i;
-
                        if (value == null)
-                               throw new ArgumentNullException ();
-
-                       start = this.length - value.Length;
-                       if (start < 0)
-                               return false;
+                               throw new ArgumentNullException ("value");
 
-                       for (i = start; i < this.length && endswith; i++)
-                               endswith = this.c_str[i] == value[i - start];
+                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       return endswith;
-               }
+                       if (value.length == 0)
+                               return startIndex;
 
-               public override bool Equals (object obj)
-               {
-                       if (!(obj is String))
-                               return false;
+                       if (startIndex == 0 && this.length == 0)
+                               return -1;
 
-                       return this == (String) obj;
-               }
+                       if (count == 0)
+                               return -1;
 
-               public bool Equals (string value)
-               {
-                       return this == value;
+                       return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
                }
 
-               public static bool Equals (string a, string b)
+               public int LastIndexOfAny (char [] anyOf)
                {
-                       return a == b;
-               }
+                       if (anyOf == null)
+                               throw new ArgumentNullException ("anyOf");
 
-               public static string Format (string format, object arg0)
-               {
-                       // FIXME: implement me
-                       return null;
+                       return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
                }
 
-               public static string Format (string format, params object[] args)
+               public int LastIndexOfAny (char [] anyOf, int startIndex)
                {
-                       // FIXME: implement me
-                       return null;
-               }
+                       if (anyOf == null) 
+                               throw new ArgumentNullException ("anyOf");
 
-               public static string Format (IFormatProvider provider, string format, params object[] args)
-               {
-                       // FIXME: implement me
-                       return null;
-               }
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-               public static string Format (string format, object arg0, object arg1)
-               {
-                       // FIXME: implement me
-                       return null;
-               }
+                       if (this.length == 0)
+                               return -1;
 
-               public static string Format (string format, object arg0, object arg1, object arg2)
-               {
-                       // FIXME: implement me
-                       return null;
+                       return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
                }
 
-               //public CharEnumerator GetEnumerator ()
-               public IEnumerator GetEnumerator ()
+               public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
                {
-                       // FIXME: implement me
-                       return null;
-               }
+                       if (anyOf == null) 
+                               throw new ArgumentNullException ("anyOf");
 
-               public override int GetHashCode ()
-               {
-                       // FIXME: implement me
-                       return 0;
-               }
+                       if (startIndex < 0 || count < 0 || startIndex > this.length || startIndex - count < -1)
+                               throw new ArgumentOutOfRangeException ();
 
-               public new Type GetType ()
-               {
-                       // FIXME: implement me
-                       return null;
-               }
+                       if (this.length == 0)
+                               return -1;
 
-               public TypeCode GetTypeCode ()
-               {
-                       // FIXME: implement me
-                       return 0;
+                       return InternalLastIndexOfAny (anyOf, startIndex, count);
                }
 
-               public int IndexOf (char value)
+               public int LastIndexOf (char value)
                {
-                       return IndexOf (value, 0, this.length);
+                       if (this.length == 0)
+                               return -1;
+                       else
+                               return LastIndexOf (value, this.length - 1, this.length);
                }
 
-               public int IndexOf (string value)
+               public int LastIndexOf (String value)
                {
-                       return IndexOf (value, 0, this.length);
+                       if (this.length == 0)
+                               /* This overload does additional checking */
+                               return LastIndexOf (value, 0, 0);
+                       else
+                               return LastIndexOf (value, this.length - 1, this.length);
                }
 
-               public int IndexOf (char value, int startIndex)
+               public int LastIndexOf (char value, int startIndex)
                {
-                       return IndexOf (value, startIndex, this.length - startIndex);
+                       return LastIndexOf (value, startIndex, startIndex + 1);
                }
 
-               public int IndexOf (string value, int startIndex)
+               public int LastIndexOf (String value, int startIndex)
                {
-                       return IndexOf (value, startIndex, this.length - startIndex);
+                       return LastIndexOf (value, startIndex, startIndex + 1);
                }
 
-               public int IndexOf (char value, int startIndex, int count)
+               /* This method is culture-insensitive */
+               public int LastIndexOf (char value, int startIndex, int count)
                {
-                       int i;
+                       if (startIndex == 0 && this.length == 0)
+                               return -1;
 
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+                       if (startIndex < 0 || count < 0)
                                throw new ArgumentOutOfRangeException ();
 
-                       for (i = startIndex; i - startIndex < count; i++)
-                               if (this.c_str[i] == value)
-                                       return i;
+                       if (startIndex >= this.length || startIndex - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ();
 
+                       for(int pos = startIndex; pos > startIndex - count; pos--) {
+                               if (this [pos] == value)
+                                       return pos;
+                       }
                        return -1;
                }
 
-               public int IndexOf (string value, int startIndex, int count)
+               /* But this one is culture-sensitive */
+               public int LastIndexOf (String value, int startIndex, int count)
                {
                        if (value == null)
-                               throw new ArgumentNullException ();
+                               throw new ArgumentNullException ("value");
 
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (value == String.Empty)
+                               return 0;
 
-                       return BoyerMoore (this.c_str, value, startIndex, count);
-#if XXX
-                       int i;
-                       for (i = startIndex; i - startIndex + value.Length <= count; ) {
-                               if (this.c_str[i] == value[0]) {
-                                       bool equal = true;
-                                       int j, nexti = 0;
-
-                                       for (j = 1; equal && value[j] != '\0'; j++) {
-                                               equal = this.c_str[i + j] == value[j];
-                                               if (this.c_str[i + j] == value[0] && nexti == 0)
-                                                       nexti = i + j;
-                                       }
+                       if (startIndex == 0 && this.length == 0)
+                               return -1;
 
-                                       if (equal)
-                                               return i;
+                       // This check is needed to match undocumented MS behaviour
+                       if (this.length == 0 && value.length > 0)
+                               return -1;
 
-                                       if (nexti != 0)
-                                               i = nexti;
-                                       else
-                                               i += j;
-                               } else
-                                       i++;
-                       }
+                       if (value.length > startIndex)
+                               return -1;
 
-                       return -1;
-#endif
-               }
+                       if (count == 0)
+                               return -1;
 
-               public int IndexOfAny (char[] values)
-               {
-                       return IndexOfAny (values, 0, this.length);
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ();
+
+                       if (count < 0 || startIndex - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ();
+
+                       return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
                }
 
-               public int IndexOfAny (char[] values, int startIndex)
+               public String PadLeft (int totalWidth)
                {
-                       return IndexOfAny (values, startIndex, this.length - startIndex);
+                       return PadLeft (totalWidth, ' ');
                }
 
-               public int IndexOfAny (char[] values, int startIndex, int count)
+               public String PadLeft (int totalWidth, char paddingChar)
                {
-                       if (values == null)
-                               throw new ArgumentNullException ();
+                       if (totalWidth < 0)
+                               throw new ArgumentException ();
 
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (totalWidth < this.length)
+                               return String.Copy (this);
 
-                       for (int i = startIndex; i < startIndex + count; i++) {
-                               for (int j = 0; j < strlen (values); j++) {
-                                       if (this.c_str[i] == values[j])
-                                               return i;
-                               }
-                       }
-
-                       return -1;
+                       return InternalPad (totalWidth, paddingChar, false);
                }
 
-               public string Insert (int startIndex, string value)
+               public String PadRight (int totalWidth)
                {
-                       char[] str;
-                       int i, j;
-
-                       if (value == null)
-                               throw new ArgumentNullException ();
-
-                       if (startIndex < 0 || startIndex > this.length)
-                               throw new ArgumentOutOfRangeException ();
-
-                       str = new char [value.Length + this.length + 1];
-                       for (i = 0; i < startIndex; i++)
-                               str[i] = this.c_str[i];
-                       for (j = 0; j < value.Length; j++)
-                               str[i + j] = value[j];
-                       for ( ; i < this.length; i++)
-                               str[i + j] = this.c_str[i];
-                       str[i + j] = '\0';
-
-                       return new String (str);
+                       return PadRight (totalWidth, ' ');
                }
 
-               [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               public extern static string Intern (string str);
+               public String PadRight (int totalWidth, char paddingChar)
+               {
+                       if (totalWidth < 0)
+                               throw new ArgumentException ();
 
-               [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               public extern static string IsInterned (string str);
+                       if (totalWidth < this.length)
+                               return String.Copy (this);
 
-               public static string Join (string separator, string[] value)
-               {
-                       return Join (separator, value, 0, value.Length);
+                       return InternalPad (totalWidth, paddingChar, true);
                }
 
-               public static string Join (string separator, string[] value, int startIndex, int count)
+               public bool StartsWith (String value)
                {
-                       // LAMESPEC: msdn doesn't specify what happens when separator is null
-                       int len, i, j, used;
-                       char[] str;
-
-                       if (separator == null || value == null)
-                               throw new ArgumentNullException ();
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+                       
+                       if (value == String.Empty)
+                               return true;
 
-                       if (startIndex + count > value.Length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (this.length < value.length)
+                               return false;
 
-                       len = 0;
-                       for (i = startIndex, used = 0; used < count; i++, used++) {
-                               if (i != startIndex)
-                                       len += separator.Length;
+                       return (0 == Compare (this, 0, value, 0 , value.length));
+               }
 
-                               len += value[i].Length;
-                       }
+               /* This method is culture insensitive */
+               public String Replace (char oldChar, char newChar)
+               {
+                       return InternalReplace (oldChar, newChar);
+               }
 
-                       // We have no elements to join?
-                       if (i == startIndex)
-                               return String.Empty;
+               /* This method is culture sensitive */
+               public String Replace (String oldValue, String newValue)
+               {
+                       if (oldValue == null)
+                               throw new ArgumentNullException ("oldValue");
 
-                       str = new char [len + 1];
-                       for (i = 0; i < value[startIndex].Length; i++)
-                               str[i] = value[startIndex][i];
+                       if (oldValue == String.Empty)
+                               throw new ArgumentException ("oldValue is the empty string.");
 
-                       used = 1;
-                       for (j = startIndex + 1; used < count; j++, used++) {
-                               int k;
+                       if (this == String.Empty)
+                               return this;
 
-                               for (k = 0; k < separator.Length; k++)
-                                       str[i++] = separator[k];
-                               for (k = 0; k < value[j].Length; k++)
-                                       str[i++] = value[j][k];
+                       if (oldValue.Length == 0 || oldValue[0] == '\0') {
+                               return(this);
                        }
-                       str[i] = '\0';
+                       
+                       if (newValue == null)
+                               newValue = String.Empty;
 
-                       return new String (str);
+                       return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
                }
 
-               public int LastIndexOf (char value)
+               public String Remove (int startIndex, int count)
                {
-                       int i = this.length;
-                       if (i == 0)
-                               return -1;
-                       for (; i >= 0; i--) {
-                               if (this.c_str[i] == value)
-                                       return i;
-                       }
+                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       return -1;
+                       return InternalRemove (startIndex, count);
                }
 
-               public int LastIndexOf (string value)
+               public String ToLower ()
                {
-                       return LastIndexOf (value, this.length, this.length);
+                       return InternalToLower (CultureInfo.CurrentCulture);
                }
 
-               public int LastIndexOf (char value, int startIndex)
+               public String ToLower (CultureInfo culture)
                {
-                       if (startIndex < 0 || startIndex > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       return InternalToLower (culture);
+               }
 
-                       for (int i = startIndex; i >= 0; i--) {
-                               if (this.c_str[i] == value)
-                                       return i;
-                       }
+               public String ToUpper ()
+               {
+                       return InternalToUpper (CultureInfo.CurrentCulture);
+               }
 
-                       return -1;
+               public String ToUpper (CultureInfo culture)
+               {
+                       return InternalToUpper (culture);
                }
 
-               public int LastIndexOf (string value, int startIndex)
+               public override String ToString ()
                {
-                       return LastIndexOf (value, startIndex, this.length);
+                       return this;
                }
 
-               public int LastIndexOf (char value, int startIndex, int count)
+               public String ToString (IFormatProvider provider)
                {
-                       if (startIndex < 0 || count < 0)
-                               throw new ArgumentOutOfRangeException ();
+                       return this;
+               }
 
-                       if (startIndex > this.length || startIndex - count < 0)
-                               throw new ArgumentOutOfRangeException ();
+               public String Trim ()
+               {
+                       return Trim (null);
+               }
 
-                       for (int i = startIndex; i >= startIndex - count; i--) {
-                               if (this.c_str[i] == value)
-                                       return i;
-                       }
+               public static String Format (String format, Object arg0)
+               {
+                       return Format (null, format, new Object[] {arg0});
+               }
 
-                       return -1;
+               public static String Format (String format, Object arg0, Object arg1)
+               {
+                       return Format (null, format, new Object[] {arg0, arg1});
                }
 
-               public int LastIndexOf (string value, int startIndex, int count)
+               public static String Format (String format, Object arg0, Object arg1, Object arg2)
                {
-                       // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
-                       //           but maybe it's the end-position in MS's implementation?
-                       //           msdn is unclear on this point. I think this is correct though.
-                       int i, len;
+                       return Format (null, format, new Object[] {arg0, arg1, arg2});
+               }
 
-                       if (value == null)
+               public static string Format (string format, params object[] args)
+               {
+                       return Format (null, format, args);
+               }
+       
+               public static string Format (IFormatProvider provider, string format, params object[] args)
+               {
+                       StringBuilder b = new StringBuilder ();
+                       FormatHelper (b, provider, format, args);
+                       return b.ToString ();
+               }
+               
+               internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
+               {
+                       if (format == null || args == null)
                                throw new ArgumentNullException ();
 
-                       if (startIndex < 0 || startIndex > this.length)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (count < 0 || startIndex - count < 0)
-                               throw new ArgumentOutOfRangeException ();
-
-                       if (value == String.Empty)
-                               return startIndex;
+                       int ptr = 0;
+                       int start = ptr;
+                       while (ptr < format.length) {
+                               char c = format[ptr ++];
 
-                       if (startIndex + value.Length > this.length) {
-                               /* just a little optimization */
-                               int start;
+                               if (c == '{') {
+                                       result.Append (format, start, ptr - start - 1);
 
-                               start = this.length - value.Length;
-                               count -= startIndex - start;
-                               startIndex = start;
-                       }
+                                       // check for escaped open bracket
 
-                       // FIXME: use a reversed-unicode-safe-Boyer-Moore?
-                       len = value.Length - 1;
-                       for (i = startIndex; i >= startIndex - count; i--) {
-                               if (this.c_str[i + len] == value[len]) {
-                                       bool equal = true;
-                                       int j;
+                                       if (format[ptr] == '{') {
+                                               start = ptr ++;
+                                               continue;
+                                       }
 
-                                       for (j = len - 1; equal && j >= 0; j--)
-                                               equal = this.c_str[i + j] == value[j];
+                                       // parse specifier
+                               
+                                       int n, width;
+                                       bool left_align;
+                                       string arg_format;
 
-                                       if (equal)
-                                               return i;
-                               }
-                       }
+                                       ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
+                                       if (n >= args.Length)
+                                               throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
 
-                       return -1;
-               }
+                                       // format argument
 
-               public int LastIndexOfAny (char[] values)
-               {
-                       return LastIndexOfAny (values, this.length, this.length);
-               }
+                                       object arg = args[n];
 
-               public int LastIndexOfAny (char[] values, int startIndex)
-               {
-                       return LastIndexOfAny (values, startIndex, startIndex);
-               }
+                                       string str;
+                                       if (arg == null)
+                                               str = "";
+                                       else if (arg is IFormattable)
+                                               str = ((IFormattable)arg).ToString (arg_format, provider);
+                                       else
+                                               str = arg.ToString ();
 
-               public int LastIndexOfAny (char[] values, int startIndex, int count)
-               {
-                       int i;
+                                       // pad formatted string and append to result
 
-                       if (values == null)
-                               throw new ArgumentNullException ();
+                                       if (width > str.length) {
+                                               string pad = new String (' ', width - str.length);
 
-                       if (startIndex < 0 || count < 0 || startIndex - count < 0)
-                               throw new ArgumentOutOfRangeException ();
+                                               if (left_align) {
+                                                       result.Append (str);
+                                                       result.Append (pad);
+                                               }
+                                               else {
+                                                       result.Append (pad);
+                                                       result.Append (str);
+                                               }
+                                       }
+                                       else
+                                               result.Append (str);
 
-                       for (i = startIndex; i >= startIndex - count; i--) {
-                               for (int j = 0; j < strlen (values); j++) {
-                                       if (this.c_str[i] == values[j])
-                                               return i;
+                                       start = ptr;
+                               }
+                               else if (c == '}' && ptr < format.length && format[ptr] == '}') {
+                                       result.Append (format, start, ptr - start - 1);
+                                       start = ptr ++;
+                               }
+                               else if (c == '}') {
+                                       throw new FormatException ("Input string was not in a correct format.");
                                }
                        }
 
-                       return -1;
+                       if (start < format.length)
+                               result.Append (format.Substring (start));
                }
 
-               public string PadLeft (int totalWidth)
+               public static String Copy (String str)
                {
-                       return PadLeft (totalWidth, ' ');
+                       if (str == null)
+                               throw new ArgumentNullException ("str");
+
+                       int length = str.length;
+
+                       String tmp = InternalAllocateStr (length);
+                       InternalStrcpy (tmp, 0, str);
+                       return tmp;
                }
 
-               public string PadLeft (int totalWidth, char padChar)
+               public static String Concat (Object obj)
                {
-                       char[] str;
-                       int i, j;
+                       if (obj == null)
+                               return String.Empty;
 
-                       if (totalWidth < 0)
-                               throw new ArgumentException ();
+                       return obj.ToString ();
+               }
 
-                       str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
-                       for (i = 0; i < totalWidth - this.length; i++)
-                               str[i] = padChar;
+               public static String Concat (Object obj1, Object obj2)
+               {
+                       string s1, s2;
 
-                       for (j = 0; j < this.length; i++, j++)
-                               str[i] = this.c_str[j];
+                       s1 = (obj1 != null) ? obj1.ToString () : null;
+                       s2 = (obj2 != null) ? obj2.ToString () : null;
+                       
+                       if (s1 == null) {
+                               if (s2 == null)
+                                       return String.Empty;
+                               else
+                                       return s2;
+                       } else if (s2 == null)
+                               return s1;
 
-                       str[i] = '\0';
+                       String tmp = InternalAllocateStr (s1.Length + s2.Length);
+                       InternalStrcpy (tmp, 0, s1);
+                       InternalStrcpy (tmp, s1.length, s2);
 
-                       return new String (str);
+                       return tmp;
                }
 
-               public string PadRight (int totalWidth)
+               public static String Concat (Object obj1, Object obj2, Object obj3)
                {
-                       return PadRight (totalWidth, ' ');
+                       string s1, s2, s3;
+                       if (obj1 == null)
+                               s1 = String.Empty;
+                       else
+                               s1 = obj1.ToString ();
+
+                       if (obj2 == null)
+                               s2 = String.Empty;
+                       else
+                               s2 = obj2.ToString ();
+
+                       if (obj3 == null)
+                               s3 = String.Empty;
+                       else
+                               s3 = obj3.ToString ();
+
+                       return Concat (s1, s2, s3);
                }
 
-               public string PadRight (int totalWidth, char padChar)
+               public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
                {
-                       char[] str;
-                       int i;
+                       string s1, s2, s3, s4;
 
-                       if (totalWidth < 0)
-                               throw new ArgumentException ();
+                       if (obj1 == null)
+                               s1 = String.Empty;
+                       else
+                               s1 = obj1.ToString ();
 
-                       str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               str[i] = this.c_str[i];
+                       if (obj2 == null)
+                               s2 = String.Empty;
+                       else
+                               s2 = obj2.ToString ();
 
-                       for ( ; i < str.Length; i++)
-                               str[i] = padChar;
+                       if (obj3 == null)
+                               s3 = String.Empty;
+                       else
+                               s3 = obj3.ToString ();
 
-                       str[i] = '\0';
+                       if (obj4 == null)
+                               s4 = String.Empty;
+                       else
+                               s4 = obj4.ToString ();
 
-                       return new String (str);
+                       return Concat (s1, s2, s3, s4);
+                       
                }
 
-               public string Remove (int startIndex, int count)
+               public static String Concat (String s1, String s2)
                {
-                       char[] str;
-                       int i, j, len;
+                       if (s1 == null) {
+                               if (s2 == null)
+                                       return String.Empty;
+                               return s2;
+                       }
 
-                       if (startIndex < 0 || count < 0 || startIndex + count > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       if (s2 == null)
+                               return s1; 
 
-                       len = this.length - count;
-                       if (len == 0)
-                               return String.Empty;
+                       String tmp = InternalAllocateStr (s1.length + s2.length);
 
-                       str = new char [len + 1];
-                       for (i = 0; i < startIndex; i++)
-                               str[i] = this.c_str[i];
-                       for (j = i + count; j < this.length; j++)
-                               str[i++] = this.c_str[j];
-                       str[i] = '\0';
+                       InternalStrcpy (tmp, 0, s1);
+                       InternalStrcpy (tmp, s1.length, s2);
 
-                       return new String (str);
+                       return tmp;
                }
 
-               public string Replace (char oldChar, char newChar)
+               public static String Concat (String s1, String s2, String s3)
                {
-                       char[] str;
-                       int i;
-
-                       str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++) {
-                               if (this.c_str[i] == oldChar)
-                                       str[i] = newChar;
-                               else
-                                       str[i] = this.c_str[i];
+                       if (s1 == null){
+                               if (s2 == null){
+                                       if (s3 == null)
+                                               return String.Empty;
+                                       return s3;
+                               } else {
+                                       if (s3 == null)
+                                               return s2;
+                               }
+                               s1 = String.Empty;
+                       } else {
+                               if (s2 == null){
+                                       if (s3 == null)
+                                               return s1;
+                                       else
+                                               s2 = String.Empty;
+                               } else {
+                                       if (s3 == null)
+                                               s3 = String.Empty;
+                               }
                        }
-                       str[i] = '\0';
 
-                       return new String (str);
+                       //return InternalConcat (s1, s2, s3);
+                       String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
+
+                       InternalStrcpy (tmp, 0, s1);
+                       InternalStrcpy (tmp, s1.length, s2);
+                       InternalStrcpy (tmp, s1.length + s2.length, s3);
+
+                       return tmp;
                }
 
-               public string Replace (string oldValue, string newValue)
+               public static String Concat (String s1, String s2, String s3, String s4)
                {
-                       // LAMESPEC: msdn doesn't specify what to do if either args is null
-                       int index, len, i, j;
-                       char[] str;
-
-                       if (oldValue == null || newValue == null)
-                               throw new ArgumentNullException ();
+                       if (s1 == null && s2 == null && s3 == null && s4 == null)
+                               return String.Empty;
 
-                       // Use IndexOf in case I later rewrite it to use Boyer-Moore
-                       index = IndexOf (oldValue, 0);
-                       if (index == -1) {
-                               // This is the easy one ;-)
-                               return Substring (0, this.length);
-                       }
+                       if (s1 == null)
+                               s1 = String.Empty;
+                       if (s2 == null)
+                               s2 = String.Empty;
+                       if (s3 == null)
+                               s3 = String.Empty;
+                       if (s4 == null)
+                               s4 = String.Empty;
 
-                       len = this.length - oldValue.Length + newValue.Length;
-                       if (len == 0)
-                               return String.Empty;
+                       String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
 
-                       str = new char [len + 1];
-                       for (i = 0; i < index; i++)
-                               str[i] = this.c_str[i];
-                       for (j = 0; j < newValue.Length; j++)
-                               str[i++] = newValue[j];
-                       for (j = index + oldValue.Length; j < this.length; j++)
-                               str[i++] = this.c_str[j];
-                       str[i] = '\0';
+                       InternalStrcpy (tmp, 0, s1);
+                       InternalStrcpy (tmp, s1.length, s2);
+                       InternalStrcpy (tmp, s1.length + s2.length, s3);
+                       InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
 
-                       return new String (str);
+                       return tmp;
                }
 
-               private int splitme (char[] separators, int startIndex)
+               public static String Concat (params Object[] args)
                {
-                       /* this is basically a customized IndexOfAny() for the Split() methods */
-                       for (int i = startIndex; i < this.length; i++) {
-                               if (separators != null) {
-                                       foreach (char sep in separators) {
-                                               if (this.c_str[i] == sep)
-                                                       return i - startIndex;
-                                       }
-                               } else if (is_lwsp (this.c_str[i])) {
-                                       return i - startIndex;
-                               }
-                       }
+                       string [] strings;
+                       int len, i, currentpos;
 
-                       return -1;
-               }
+                       if (args == null)
+                               throw new ArgumentNullException ("args");
 
-               public string[] Split (params char[] separator)
-               {
-                       /**
-                        * split:
-                        * @separator: delimiting chars or null to split on whtspc
-                        *
-                        * Returns: 1. An array consisting of a single
-                        * element (@this) if none of the delimiting
-                        * chars appear in @this. 2. An array of
-                        * substrings which are delimited by one of
-                        * the separator chars. 3. An array of
-                        * substrings separated by whitespace if
-                        * @separator is null. The Empty string should
-                        * be returned wherever 2 delimiting chars are
-                        * adjacent.
-                        **/
-                       // FIXME: would using a Queue be better?
-                       string[] strings;
-                       ArrayList list;
-                       int index, len;
-
-                       list = new ArrayList ();
-                       for (index = 0, len = 0; index < this.length; index += len + 1) {
-                               len = splitme (separator, index);
-                               len = len > -1 ? len : this.length - index;
-                               if (len == 0) {
-                                       list.Add (String.Empty);
-                               } else {
-                                       char[] str;
-                                       int i;
+                       strings = new string [args.Length];
+                       len = 0;
+                       i = 0;
+                       foreach (object arg in args) {
+                               /* use Empty for each null argument */
+                               if (arg == null)
+                                       strings[i] = String.Empty;
+                               else
+                                       strings[i] = arg.ToString ();
+                               len += strings[i].length;
+                               i++;
+                       }
 
-                                       str = new char [len + 1];
-                                       for (i = 0; i < len; i++)
-                                               str[i] = this.c_str[index + i];
-                                       str[i] = '\0';
+                       if (len == 0)
+                               return String.Empty;
 
-                                       list.Add (new String (str));
-                               }
-                       }
+                       currentpos = 0;
 
-                       strings = new string [list.Count];
-                       if (list.Count == 1) {
-                               /* special case for an array holding @this */
-                               strings[0] = this;
-                       } else {
-                               for (index = 0; index < list.Count; index++)
-                                       strings[index] = (string) list[index];
+                       String tmp = InternalAllocateStr (len);
+                       for (i = 0; i < strings.Length; i++) {
+                               InternalStrcpy (tmp, currentpos, strings[i]);
+                               currentpos += strings[i].length;
                        }
 
-                       return strings;
+                       return tmp;
                }
 
-               public string[] Split (char[] separator, int maxCount)
+               public static String Concat (params String[] values)
                {
-                       // FIXME: what to do if maxCount <= 0?
-                       // FIXME: would using Queue be better than ArrayList?
-                       string[] strings;
-                       ArrayList list;
-                       int index, len, used;
-
-                       used = 0;
-                       list = new ArrayList ();
-                       for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
-                               len = splitme (separator, index);
-                               len = len > -1 ? len : this.length - index;
-                               if (len == 0) {
-                                       list.Add (String.Empty);
-                               } else {
-                                       char[] str;
-                                       int i;
+                       int len, i, currentpos;
 
-                                       str = new char [len + 1];
-                                       for (i = 0; i < len; i++)
-                                               str[i] = this.c_str[index + i];
-                                       str[i] = '\0';
+                       if (values == null)
+                               throw new ArgumentNullException ("values");
 
-                                       list.Add (new String (str));
-                               }
-                               used++;
-                       }
+                       len = 0;
+                       foreach (string value in values)
+                               len += value != null ? value.length : 0;
 
-                       /* fit the remaining chunk of the @this into it's own element */
-                       if (index != this.length) {
-                               char[] str;
-                               int i;
+                       if (len == 0)
+                               return String.Empty;
 
-                               str = new char [this.length - index + 1];
-                               for (i = index; i < this.length; i++)
-                                       str[i - index] = this.c_str[i];
-                               str[i - index] = '\0';
+                       currentpos = 0;
 
-                               list.Add (new String (str));
-                       }
+                       String tmp = InternalAllocateStr (len);
+                       for (i = 0; i < values.Length; i++) {
+                               if (values[i] == null)
+                                       continue;
 
-                       strings = new string [list.Count];
-                       if (list.Count == 1) {
-                               /* special case for an array holding @this */
-                               strings[0] = this;
-                       } else {
-                               for (index = 0; index < list.Count; index++)
-                                       strings[index] = (string) list[index];
-                       }
+                               InternalStrcpy (tmp, currentpos, values[i]);
+                               currentpos += values[i].length;
+                       }       
 
-                       return strings;
+                       return tmp;
                }
 
-               public bool StartsWith (string value)
+               public String Insert (int startIndex, String value)
                {
-                       bool startswith = true;
-                       int i;
-
                        if (value == null)
-                               throw new ArgumentNullException ();
+                               throw new ArgumentNullException ("value");
 
-                       if (value.Length > this.length)
-                               return false;
-
-                       for (i = 0; i < value.Length && startswith; i++)
-                               startswith = startswith && value[i] == this.c_str[i];
+                       if (startIndex < 0 || startIndex > this.length)
+                               throw new ArgumentOutOfRangeException ();
 
-                       return startswith;
+                       return InternalInsert (startIndex, value);
                }
 
-               public string Substring (int startIndex)
+
+               public static string Intern (string str)
                {
-                       char[] str;
-                       int i, len;
+                       if (str == null)
+                               throw new ArgumentNullException ("str");
 
-                       if (startIndex < 0 || startIndex > this.length)
-                               throw new ArgumentOutOfRangeException ();
+                       return InternalIntern (str);
+               }
 
-                       len = this.length - startIndex;
-                       if (len == 0)
-                               return String.Empty;
+               public static string IsInterned (string str)
+               {
+                       if (str == null)
+                               throw new ArgumentNullException ("str");
 
-                       str = new char [len + 1];
-                       for (i = startIndex; i < this.length; i++)
-                               str[i - startIndex] = this.c_str[i];
-                       str[i] = '\0';
+                       return InternalIsInterned (str);
+               }
+       
+               public static string Join (string separator, string [] value)
+               {
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
 
-                       return new String (str);
+                       return Join (separator, value, 0, value.Length);
                }
 
-               public string Substring (int startIndex, int length)
+               public static string Join (string separator, string[] value, int startIndex, int count)
                {
-                       char[] str;
-                       int i;
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
 
-                       if (startIndex < 0 || length < 0 || startIndex + length > this.length)
+                       if (startIndex + count > value.Length)
                                throw new ArgumentOutOfRangeException ();
 
-                       if (length == 0)
+                       if (startIndex == value.Length)
                                return String.Empty;
 
-                       str = new char [length + 1];
-                       for (i = startIndex; i < startIndex + length; i++)
-                               str[i - startIndex] = this.c_str[i];
-                       str[i] = '\0';
-
-                       return new String (str);
+                       return InternalJoin (separator, value, startIndex, count);
                }
 
-               public bool ToBoolean (IFormatProvider provider)
+               bool IConvertible.ToBoolean (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return false;
+                       return Convert.ToBoolean (this, provider);
                }
 
-               public byte ToByte (IFormatProvider provider)
+               byte IConvertible.ToByte (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return (byte) '\0';
+                       return Convert.ToByte (this, provider);
                }
 
-               public char ToChar (IFormatProvider provider)
+               char IConvertible.ToChar (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return '\0';
+                       return Convert.ToChar (this, provider);
                }
 
-               public char[] ToCharArray ()
+               DateTime IConvertible.ToDateTime (IFormatProvider provider)
                {
-                       return ToCharArray (0, this.length);
+                       return Convert.ToDateTime (this, provider);
                }
 
-               public char[] ToCharArray (int startIndex, int length)
+               decimal IConvertible.ToDecimal (IFormatProvider provider)
                {
-                       char[] chars;
-                       int i;
-
-                       if (startIndex < 0 || length < 0 || startIndex + length > this.length)
-                               throw new ArgumentOutOfRangeException ();
-
-                       chars = new char [length + 1];
-                       for (i = startIndex; i < length; i++)
-                               chars[i - startIndex] = this.c_str[i];
-
-                       chars[length] = '\0';
-
-                       return chars;
+                       return Convert.ToDecimal (this, provider);
                }
 
-               public DateTime ToDateTime (IFormatProvider provider)
+               double IConvertible.ToDouble (IFormatProvider provider)
                {
-                       // FIXME: implement me
-
-                       return new DateTime (0);
+                       return Convert.ToDouble (this, provider);
                }
 
-               public decimal ToDecimal (IFormatProvider provider)
+               short IConvertible.ToInt16 (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0.0M;
+                       return Convert.ToInt16 (this, provider);
                }
 
-               public double ToDouble (IFormatProvider provider)
+               int IConvertible.ToInt32 (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0.0;
+                       return Convert.ToInt32 (this, provider);
                }
 
-               public short ToInt16 (IFormatProvider provider)
+               long IConvertible.ToInt64 (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0;
+                       return Convert.ToInt64 (this, provider);
                }
-
-               public int ToInt32 (IFormatProvider provider)
+       
+               [CLSCompliant (false)]
+               sbyte IConvertible.ToSByte (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0;
+                       return Convert.ToSByte (this, provider);
                }
 
-               public long ToInt64 (IFormatProvider provider)
+               float IConvertible.ToSingle (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0;
+                       return Convert.ToSingle (this, provider);
                }
 
-               public string ToLower ()
+               string IConvertible.ToString (IFormatProvider format)
                {
-                       char[] str;
-                       int i;
-
-                       str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               str[i] = Char.ToLower (this.c_str[i]);
-                       str[i] = '\0';
-
-                       return new String (str);
+                       return this;
                }
 
-               public string ToLower (CultureInfo culture)
+               object IConvertible.ToType (Type conversionType, IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return null;
+                       return Convert.ToType (this, conversionType,  provider);
                }
 
-               public sbyte ToSByte (IFormatProvider provider)
+               [CLSCompliant (false)]
+               ushort IConvertible.ToUInt16 (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0;
+                       return Convert.ToUInt16 (this, provider);
                }
 
-               public float ToSingle (IFormatProvider provider)
+               [CLSCompliant (false)]
+               uint IConvertible.ToUInt32 (IFormatProvider provider)
                {
-                       // FIXME: implement me
-                       return 0.0F;
+                       return Convert.ToUInt32 (this, provider);
                }
 
-               public override string ToString ()
+               [CLSCompliant (false)]
+               ulong IConvertible.ToUInt64 (IFormatProvider provider)
                {
-                       return Substring (0, this.length);
+                       return Convert.ToUInt64 (this, provider);
                }
 
-               public string ToString (IFormatProvider format)
+               TypeCode IConvertible.GetTypeCode ()
                {
-                       // FIXME: implement me
-                       return null;
+                       return TypeCode.String;
                }
 
-               public object ToType (Type conversionType, IFormatProvider provider)
-               {
-                       // FIXME: implement me
-                       return null;
+               public int Length {
+                       get {
+                               return length;
+                       }
                }
 
-               public ushort ToUInt16 (IFormatProvider provider)
+               public CharEnumerator GetEnumerator ()
                {
-                       // FIXME: implement me
-                       return 0;
+                       return new CharEnumerator (this);
                }
 
-               public uint ToUInt32 (IFormatProvider provider)
+               IEnumerator IEnumerable.GetEnumerator ()
                {
-                       // FIXME: implement me
-                       return 0;
+                       return new CharEnumerator (this);
                }
 
-               public ulong ToUInt64 (IFormatProvider provider)
+               private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
+                                                         out bool left_align, out string format)
                {
-                       // FIXME: implement me
-                       return 0;
-               }
+                       // parses format specifier of form:
+                       //   N,[\ +[-]M][:F]}
+                       //
+                       // where:
 
-               public string ToUpper ()
-               {
-                       char[] str;
-                       int i;
+                       try {
+                               // N = argument number (non-negative integer)
 
-                       str = new char [this.length + 1];
-                       for (i = 0; i < this.length; i++)
-                               str[i] = Char.ToUpper (this.c_str[i]);
-                       str[i] = '\0';
+                               n = ParseDecimal (str, ref ptr);
+                               if (n < 0)
+                                       throw new FormatException ("Input string was not in a correct format.");
 
-                       return new String (str);
-               }
+                               // M = width (non-negative integer)
 
-               public string ToUpper (CultureInfo culture)
-               {
-                       // FIXME: implement me
-                       return null;
-               }
+                               if (str[ptr] == ',') {
+                                       // White space between ',' and number or sign.
+                                       int start = ++ptr;
+                                       while (Char.IsWhiteSpace (str [ptr]))
+                                               ++ptr;
 
-               public string Trim ()
-               {
-                       return Trim (null);
-               }
+                                       format = str.Substring (start, ptr - start);
 
-               public string Trim (params char[] trimChars)
-               {
-                       int begin, end;
-                       bool matches;
+                                       left_align = (str [ptr] == '-');
+                                       if (left_align)
+                                               ++ ptr;
 
-                       matches = true;
-                       for (begin = 0; matches && begin < this.length; begin++) {
-                               if (trimChars != null) {
-                                       matches = false;
-                                       foreach (char c in trimChars) {
-                                               matches = this.c_str[begin] == c;
-                                               if (matches)
-                                                       break;
-                                       }
-                               } else {
-                                       matches = is_lwsp (this.c_str[begin]);
+                                       width = ParseDecimal (str, ref ptr);
+                                       if (width < 0)
+                                               throw new FormatException ("Input string was not in a correct format.");
                                }
-                       }
-
-                       matches = true;
-                       for (end = this.length-1; end > begin; end--) {
-                               if (trimChars != null) {
-                                       matches = false;
-                                       foreach (char c in trimChars) {
-                                               matches = this.c_str[end] == c;
-                                               if (matches)
-                                                       break;
-                                       }
-                               } else {
-                                       matches = is_lwsp (this.c_str[end]);
+                               else {
+                                       width = 0;
+                                       left_align = false;
+                                       format = "";
                                }
-                       }
 
-                       if (begin == end)
-                               return String.Empty;
+                               // F = argument format (string)
 
-                       return Substring (begin, end - begin);
+                               if (str[ptr] == ':') {
+                                       int start = ++ ptr;
+                                       while (str[ptr] != '}')
+                                               ++ ptr;
+
+                                       format += str.Substring (start, ptr - start);
+                               }
+                               else
+                                       format = null;
+
+                               if (str[ptr ++] != '}')
+                                       throw new FormatException ("Input string was not in a correct format.");
+                       }
+                       catch (IndexOutOfRangeException) {
+                               throw new FormatException ("Input string was not in a correct format.");
+                       }
                }
 
-               public string TrimEnd (params char[] trimChars)
+               private static int ParseDecimal (string str, ref int ptr)
                {
-                       bool matches = true;
-                       int end;
+                       int p = ptr;
+                       int n = 0;
+                       while (true) {
+                               char c = str[p];
+                               if (c < '0' || '9' < c)
+                                       break;
 
-                       for (end = this.length; end > 0; end--) {
-                               if (trimChars != null) {
-                                       matches = false;
-                                       foreach (char c in trimChars) {
-                                               matches = this.c_str[end] == c;
-                                               if (matches)
-                                                       break;
-                                       }
-                               } else {
-                                       matches = is_lwsp (this.c_str[end]);
-                               }
+                               n = n * 10 + c - '0';
+                               ++ p;
                        }
 
-                       if (end == 0)
-                               return String.Empty;
+                       if (p == ptr)
+                               return -1;
 
-                       return Substring (0, end);
+                       ptr = p;
+                       return n;
                }
 
-               public string TrimStart (params char[] trimChars)
+               internal unsafe void InternalSetChar (int idx, char val)
                {
-                       bool matches = true;
-                       int begin;
+                       if ((uint) idx >= (uint) Length)
+                               throw new ArgumentOutOfRangeException ("idx");
 
-                       for (begin = 0; matches && begin < this.length; begin++) {
-                               if (trimChars != null) {
-                                       matches = false;
-                                       foreach (char c in trimChars) {
-                                               matches = this.c_str[begin] == c;
-                                               if (matches)
-                                                       break;
-                                       }
-                               } else {
-                                       matches = is_lwsp (this.c_str[begin]);
-                               }
+                       fixed (char * pStr = &start_char) 
+                       {
+                               pStr [idx] = val;
                        }
-
-                       if (begin == this.length)
-                               return String.Empty;
-
-                       return Substring (begin, this.length - begin);
                }
 
-               // Operators
-               public static bool operator ==(string a, string b)
+               internal unsafe void InternalSetLength (int newLength)
                {
-                       if (a.length != b.length)
-                               return false;
+                       if (newLength > length)
+                               throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
 
-                       int l = a.length;
-                       for (int i = 0; i < l; i++)
-                               if (a.c_str [i] != b.c_str [i])
-                                       return false;
+                       length = newLength;
 
-                       return true;
+                       // zero terminate, we can pass string objects directly via pinvoke
+                       fixed (char * pStr = &start_char) {
+                               pStr [length] = '\0';
+                       }
                }
 
-               public static bool operator !=(string a, string b)
-               {
-                       return !(a == b);
-               }
+               [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+               unsafe public extern String (char *value);
+
+               [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+               unsafe public extern String (char *value, int startIndex, int length);
+
+               [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+               unsafe public extern String (sbyte *value);
+
+               [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+               unsafe public extern String (sbyte *value, int startIndex, int length);
+
+               [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+               unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               public extern String (char [] val, int startIndex, int length);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               public extern String (char [] val);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               public extern String (char c, int count);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               public extern override int GetHashCode ();
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalInsert (int sourceIndex, String value);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalReplace (char oldChar, char newChar);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalRemove (int sIndex, int count);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String[] InternalSplit (char[] separator, int count);
+
+               [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);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalPad (int width, char chr, bool right);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalToLower (CultureInfo culture);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern String InternalToUpper (CultureInfo culture);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static String InternalAllocateStr (int length);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static void InternalStrcpy (String dest, int destPos, String src);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static string InternalIntern (string str);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static string InternalIsInterned (string str);
        }
 }