Wed Apr 10 21:20:19 CEST 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mcs / class / corlib / System / String.cs
1 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 //
3 // System.String.cs
4 //
5 // Authors:
6 //   Jeffrey Stedfast (fejj@ximian.com)
7 //   Dan Lewis (dihlewis@yahoo.co.uk)
8 //
9 // (C) 2001 Ximian, Inc.  http://www.ximian.com
10 //
11
12 // FIXME: from what I gather from msdn, when a function is to return an empty string
13 //        we should be returning this.Empty - some methods do this and others don't.
14
15 // FIXME: I didn't realise until later that `string' has a .Length method and so
16 //        I am missing some proper bounds-checking in some methods. Find these
17 //        instances and throw the ArgumentOutOfBoundsException at the programmer.
18 //        I like pelting programmers with ArgumentOutOfBoundsException's :-)
19
20 // FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
21 //        need to be made unicode aware.
22
23 // FIXME: when you have a char carr[], does carr.Length include the terminating null char?
24
25 using System;
26 using System.Text;
27 using System.Collections;
28 using System.Globalization;
29 using System.Runtime.CompilerServices;
30
31 namespace System {
32
33         //[DefaultMemberName("Chars")]
34         [Serializable]
35         public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
36                 public static readonly string Empty = "";
37                 private char[] c_str;
38                 private int length;
39
40                 // Constructors
41
42                 internal String (int storage)
43                 {
44                         if (storage < 0)
45                                 throw new ArgumentOutOfRangeException ();
46                         length = storage;
47                         c_str = new char [storage];
48                 }
49                 
50                 [CLSCompliant(false)]
51                 unsafe public String (char *value)
52                 {
53                         int i;
54
55                         // FIXME: can I do value.Length here?
56                         if (value == null) {
57                                 this.length = 0;
58                         } else {
59                                 for (i = 0; *(value + i) != '\0'; i++);
60                                 this.length = i;
61                         }
62
63                         this.c_str = new char [this.length + 1];
64                         for (i = 0; i < this.length; i++)
65                                 this.c_str[i] = *(value + i);
66                 }
67
68                 public String (char[] value)
69                 {
70                         int i;
71
72                         // FIXME: value.Length includes the terminating null char?
73                         this.length = value != null ? strlen (value): 0;
74                         this.c_str = new char [this.length + 1];
75                         for (i = 0; i < this.length; i++)
76                                 this.c_str[i] = value[i];
77                 }
78
79                 [CLSCompliant(false)]
80                 unsafe public String (sbyte *value)
81                 {
82                         // FIXME: consider unicode?
83                         int i;
84
85                         // FIXME: can I do value.Length here? */
86                         if (value == null) {
87                                 this.length = 0;
88                         } else {
89                                 for (i = 0; *(value + i) != '\0'; i++);
90                                 this.length = i;
91                         }
92
93                         this.c_str = new char [this.length + 1];
94                         for (i = 0; i < this.length; i++)
95                                 this.c_str[i] = (char) *(value + i);
96                 }
97
98                 public String (char c, int count)
99                 {
100                         int i;
101
102                         this.length = count;
103                         this.c_str = new char [count + 1];
104                         for (i = 0; i < count; i++)
105                                 this.c_str[i] = c;
106                 }
107
108                 [CLSCompliant(false)]
109                 unsafe public String (char *value, int startIndex, int length)
110                 {
111                         int i;
112
113                         if (value == null && startIndex != 0 && length != 0)
114                                 throw new ArgumentNullException ();
115
116                         if (startIndex < 0 || length < 0)
117                                 throw new ArgumentOutOfRangeException ();
118
119                         this.length = length;
120                         this.c_str = new char [length + 1];
121                         for (i = 0; i < length; i++)
122                                 this.c_str[i] = *(value + startIndex + i);
123                 }
124
125                 public String (char[] value, int startIndex, int length)
126                 {
127                         int i;
128
129                         if (value == null && startIndex != 0 && length != 0)
130                                 throw new ArgumentNullException ();
131
132                         if (startIndex < 0 || length < 0)
133                                 throw new ArgumentOutOfRangeException ();
134
135                         this.length = length;
136                         this.c_str = new char [length + 1];
137                         for (i = 0; i < length; i++)
138                                 this.c_str[i] = value[startIndex + i];
139                 }
140
141                 [CLSCompliant(false)]
142                 unsafe public String (sbyte *value, int startIndex, int length)
143                 {
144                         // FIXME: consider unicode?
145                         int i;
146
147                         if (value == null && startIndex != 0 && length != 0)
148                                 throw new ArgumentNullException ();
149
150                         if (startIndex < 0 || length < 0)
151                                 throw new ArgumentOutOfRangeException ();
152
153                         this.length = length;
154                         this.c_str = new char [length + 1];
155                         for (i = 0; i < length; i++)
156                                 this.c_str[i] = (char) *(value + startIndex + i);
157                 }
158
159                 [CLSCompliant(false)][MonoTODO]
160                 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
161                 {
162                         // FIXME: implement me
163                 }
164
165                 // FIXME: is there anything we need to do here?
166                 /*
167                 ~String ()
168                 {
169                         base.Finalize ();
170                 }
171                 */
172
173                 // Properties
174                 public int Length {
175                         get {
176                                 return this.length;
177                         }
178                 }
179
180                 [IndexerName("Chars")]
181                 public char this [int index] {
182                         get {
183                                 if (index >= this.length)
184                                         throw new ArgumentOutOfRangeException ();
185
186                                 return this.c_str[index];
187                         }
188                 }
189
190                 // Private helper methods
191                 private static int strlen (char[] str)
192                 {
193                         // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
194                         return str.Length;
195                 }
196
197                 [MonoTODO]
198                 private static char tolowerordinal (char c)
199                 {
200                         // FIXME: implement me
201                         return c;
202                 }
203
204                 private static bool is_lwsp (char c)
205                 {
206                         /* this comes from the msdn docs for String.Trim() */
207                         if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
208                             (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
209                                 return true;
210                         else
211                                 return false;
212                 }
213
214                 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
215                 {
216                         /* (hopefully) Unicode-safe Boyer-Moore implementation */
217                         int[] skiptable = new int[65536];  /* our unicode-safe skip-table */
218                         int h, n, he, ne, hc, nc, i;
219
220                         if (haystack == null || needle == null)
221                                 throw new ArgumentNullException ();
222
223                         /* if the search buffer is shorter than the pattern buffer, we can't match */
224                         if (count < needle.length)
225                                 return -1;
226
227                         /* return an instant match if the pattern is 0-length */
228                         if (needle.length == 0)
229                                 return startIndex;
230
231                         /* set a pointer at the end of each string */
232                         ne = needle.length - 1;      /* position of char before '\0' */
233                         he = startIndex + count;     /* position of last valid char */
234
235                         /* init the skip table with the pattern length */
236                         nc = needle.length;
237                         for (i = 0; i < 65536; i++)
238                                 skiptable[i] = nc;
239
240                         /* set the skip value for the chars that *do* appear in the
241                          * pattern buffer (needle) to the distance from the index to
242                          * the end of the pattern buffer. */
243                         for (nc = 0; nc < ne; nc++)
244                                 skiptable[(int) needle[nc]] = ne - nc;
245
246                         h = startIndex;
247                         while (count >= needle.length) {
248                                 hc = h + needle.length - 1;  /* set the haystack compare pointer */
249                                 nc = ne;                     /* set the needle compare pointer */
250
251                                 /* work our way backwards until they don't match */
252                                 for (i = 0; nc > 0; nc--, hc--, i++)
253                                         if (needle[nc] != haystack[hc])
254                                                 break;
255
256                                 if (needle[nc] != haystack[hc]) {
257                                         n = skiptable[(int) haystack[hc]] - i;
258                                         h += n;
259                                         count -= n;
260                                 } else
261                                         return h;
262                         }
263
264                         return -1;
265                 }
266
267                 // Methods
268                 [MonoTODO]
269                 public object Clone ()
270                 {
271                         // FIXME: implement me
272                         return null;
273                 }
274
275                 const int StringCompareModeDirect = 0;
276                 const int StringCompareModeCaseInsensitive = 1;
277                 const int StringCompareModeOrdinal = 2;
278
279                 internal static int _CompareGetLength (string strA, string strB)
280                 {
281                         if ((strA == null) || (strB == null))
282                                         return 0;
283                                 else
284                                 return Math.Max (strA.Length, strB.Length);
285                 }
286
287                 internal static int _CompareChar (char chrA, char chrB, CultureInfo culture,
288                                                   int mode)
289                 {
290                         int result = 0;
291
292                         switch (mode) {
293                         case StringCompareModeDirect:
294                                 // FIXME: We should do a culture based comparision here,
295                                 //        but for the moment let's do it by hand.
296                                 //        In the microsoft runtime, uppercase letters
297                                 //        sort after lowercase letters in the default
298                                 //        culture.
299                                 if (Char.IsUpper (chrA) && Char.IsLower (chrB))
300                                 return 1;
301                                 else if (Char.IsLower (chrA) && Char.IsUpper (chrB))
302                                 return -1;
303                                 result = (int) (chrA - chrB);
304                                 break;
305                         case StringCompareModeCaseInsensitive:
306                                 result = (int) (Char.ToLower (chrA) - Char.ToLower (chrB));
307                                 break;
308                         case StringCompareModeOrdinal:
309                                 result = (int) (tolowerordinal (chrA) - tolowerordinal (chrB));
310                                 break;
311                         }
312
313                         if (result == 0)
314                                         return 0;
315                         else if (result < 0)
316                                         return -1;
317                         else
318                                 return 1;
319                 }
320
321                 [MonoTODO]
322                 internal static int _Compare (string strA, int indexA, string strB, int indexB,
323                                               int length, CultureInfo culture,
324                                               int mode)
325
326                 {
327                         int i;
328
329                         /* When will the hurting stop!?!? */
330                         if (strA == null) {
331                                 if (strB == null)
332                                         return 0;
333                                 else
334                                         return -1;
335                         } else if (strB == null)
336                                 return 1;
337
338                         if (length < 0 || indexA < 0 || indexB < 0)
339                                 throw new ArgumentOutOfRangeException ();
340
341                         if (indexA > strA.Length || indexB > strB.Length)
342                                 throw new ArgumentOutOfRangeException ();
343
344                         // FIXME: Implement culture
345                         if (culture != null)
346                                 throw new NotImplementedException ();
347
348                         for (i = 0; i < length - 1; i++) {
349                                 if ((indexA+i >= strA.Length) || (indexB+i >= strB.Length))
350                                         break;
351
352                                 if (_CompareChar (strA[indexA+i], strB[indexB+i], culture, mode) != 0)
353                                         break;
354                 }
355
356                         if (indexA+i >= strA.Length) {
357                                 if (indexB+i >= strB.Length)
358                                         return 0;
359                                 else
360                                         return -1;
361                         } else if (indexB+i >= strB.Length)
362                                 return 1;
363
364                         return _CompareChar (strA[indexA+i], strB[indexB+i], culture, mode);
365                 }
366
367
368                 public static int Compare (string strA, string strB)
369                 {
370                         return Compare (strA, strB, false);
371                 }
372
373                 public static int Compare (string strA, string strB, bool ignoreCase)
374                 {
375                         return Compare (strA, strB, ignoreCase, null);
376                         }
377
378                 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
379                 {
380                         return Compare (strA, 0, strB, 0,
381                                         _CompareGetLength (strA, strB),
382                                         ignoreCase, culture);
383                 }
384
385                 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
386                 {
387                         return  Compare (strA, indexA, strB, indexB, length, false);
388                 }
389
390                 public static int Compare (string strA, int indexA, string strB, int indexB,
391                                            int length, bool ignoreCase)
392                 {
393                         return Compare (strA, indexA, strB, indexB, length, ignoreCase, null);
394                 }
395
396                 public static int Compare (string strA, int indexA, string strB, int indexB,
397                                            int length, bool ignoreCase, CultureInfo culture)
398                 {
399                         int mode;
400
401                         mode = ignoreCase ? StringCompareModeCaseInsensitive :
402                                 StringCompareModeDirect;
403
404                         return _Compare (strA, indexA, strB, indexB, length, culture, mode);
405                 }
406
407                 public static int CompareOrdinal (string strA, string strB)
408                 {
409                         return CompareOrdinal (strA, 0, strB, 0, _CompareGetLength (strA, strB));
410                         }
411
412                 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
413                                                   int length)
414                 {
415                         return _Compare (strA, indexA, strB, indexB, length, null,
416                                          StringCompareModeOrdinal);
417                 }
418
419                 public int CompareTo (object obj)
420                 {
421                         return Compare (this, obj == null ? null : obj.ToString ());
422                 }
423
424                 public int CompareTo (string str)
425                 {
426                         return Compare (this, str);
427                 }
428
429                 public static string Concat (object arg)
430                 {
431                         return arg != null ? arg.ToString () : String.Empty;
432                 }
433
434                 public static string Concat (params object[] args)
435                 {
436                         string[] strings;
437                         char[] str;
438                         int len, i;
439
440                         if (args == null)
441                                 throw new ArgumentNullException ();
442
443                         strings = new string [args.Length];
444                         len = 0;
445                         i = 0;
446                         foreach (object arg in args) {
447                                 /* use Empty for each null argument */
448                                 if (arg == null)
449                                         strings[i] = String.Empty;
450                                 else
451                                         strings[i] = arg.ToString ();
452                                 len += strings[i].length;
453                                 i++;
454                         }
455
456                         if (len == 0)
457                                 return String.Empty;
458
459                         String res = new String (len);
460                         str = res.c_str;
461                         i = 0;
462                         for (int j = 0; j < strings.Length; j++)
463                                 for (int k = 0; k < strings[j].length; k++)
464                                         str[i++] = strings[j].c_str[k];
465
466                         return res;
467                 }
468
469                 public static string Concat (params string[] values)
470                 {
471                         int len, i;
472                         char[] str;
473
474                         if (values == null)
475                                 throw new ArgumentNullException ();
476
477                         len = 0;
478                         foreach (string value in values)
479                                 len += value != null ? value.Length : 0;
480
481                         if (len == 0)
482                                 return String.Empty;
483
484                         String res = new String (len);
485                         str = res.c_str;
486                         i = 0;
487                         foreach (string value in values) {
488                                 if (value == null)
489                                         continue;
490
491                                 for (int j = 0; j < value.length; j++)
492                                         str[i++] = value.c_str[j];
493                         }
494
495                         return res;
496                 }
497
498                 public static string Concat (object arg0, object arg1)
499                 {
500                         string str0 = arg0 != null ? arg0.ToString () : String.Empty;
501                         string str1 = arg1 != null ? arg1.ToString () : String.Empty;
502
503                         return Concat (str0, str1);
504                 }
505
506                 public static string Concat (string str0, string str1)
507                 {
508                         char[] concat;
509                         int i, j, len;
510
511                         if (str0 == null)
512                                 str0 = String.Empty;
513                         if (str1 == null)
514                                 str1 = String.Empty;
515
516                         len = str0.length + str1.length;
517                         if (len == 0)
518                                 return String.Empty;
519
520                         String res = new String (len);
521
522                         concat = res.c_str;
523                         for (i = 0; i < str0.length; i++)
524                                 concat[i] = str0.c_str[i];
525                         for (j = 0 ; j < str1.length; j++)
526                                 concat[i + j] = str1.c_str[j];
527
528                         return res;
529                 }
530
531                 public static string Concat (object arg0, object arg1, object arg2)
532                 {
533                         string str0 = arg0 != null ? arg0.ToString () : String.Empty;
534                         string str1 = arg1 != null ? arg1.ToString () : String.Empty;
535                         string str2 = arg2 != null ? arg2.ToString () : String.Empty;
536
537                         return Concat (str0, str1, str2);
538                 }
539
540                 public static string Concat (string str0, string str1, string str2)
541                 {
542                         char[] concat;
543                         int i, j, k, len;
544
545                         if (str0 == null)
546                                 str0 = String.Empty;
547                         if (str1 == null)
548                                 str1 = String.Empty;
549                         if (str2 == null)
550                                 str2 = String.Empty;
551
552                         len = str0.length + str1.length + str2.length;
553                         if (len == 0)
554                                 return String.Empty;
555
556                         String res = new String (len);
557
558                         concat = res.c_str;
559                         for (i = 0; i < str0.length; i++)
560                                 concat[i] = str0.c_str[i];
561                         for (j = 0; j < str1.length; j++)
562                                 concat[i + j] = str1.c_str[j];
563                         for (k = 0; k < str2.length; k++)
564                                 concat[i + j + k] = str2.c_str[k];
565
566                         return res;
567                 }
568
569                 public static string Concat (string str0, string str1, string str2, string str3)
570                 {
571                         char[] concat;
572                         int i, j, k, l, len;
573
574                         if (str0 == null)
575                                 str0 = String.Empty;
576                         if (str1 == null)
577                                 str1 = String.Empty;
578                         if (str2 == null)
579                                 str2 = String.Empty;
580                         if (str3 == null)
581                                 str3 = String.Empty;
582
583                         len = str0.length + str1.length + str2.length + str3.length;
584                         if (len == 0)
585                                 return String.Empty;
586                         String res = new String (len);
587
588                         concat = res.c_str;
589                         for (i = 0; i < str0.length; i++)
590                                 concat[i] = str0.c_str[i];
591                         for (j = 0; j < str1.length; j++)
592                                 concat[i + j] = str1.c_str[j];
593                         for (k = 0; k < str2.length; k++)
594                                 concat[i + j + k] = str2.c_str[k];
595                         for (l = 0; l < str3.length; l++)
596                                 concat[i + j + k + l] = str3.c_str[l];
597
598                         return res;
599                 }
600
601                 public static string Copy (string str)
602                 {
603                         // FIXME: how do I *copy* a string if I can only have 1 of each?
604                         if (str == null)
605                                 throw new ArgumentNullException ();
606
607                         return str;
608                 }
609
610                 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
611                 {
612                         // LAMESPEC: should I null-terminate?
613                         int i;
614
615                         if (destination == null)
616                                 throw new ArgumentNullException ();
617
618                         if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
619                                 throw new ArgumentOutOfRangeException ();
620
621                         if (sourceIndex + count > this.length)
622                                 throw new ArgumentOutOfRangeException ();
623
624                         if (destinationIndex + count > destination.Length)
625                                 throw new ArgumentOutOfRangeException ();
626
627                         for (i = 0; i < count; i++)
628                                 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
629                 }
630
631                 public bool EndsWith (string value)
632                 {
633                         bool endswith = true;
634                         int start, i;
635
636                         if (value == null)
637                                 throw new ArgumentNullException ();
638
639                         start = this.length - value.length;
640                         if (start < 0)
641                                 return false;
642
643                         for (i = start; i < this.length && endswith; i++)
644                                 endswith = this.c_str[i] == value.c_str[i - start];
645
646                         return endswith;
647                 }
648
649                 public override bool Equals (object obj)
650                 {
651                         if (!(obj is String))
652                                 return false;
653
654                         return this == (String) obj;
655                 }
656
657                 public bool Equals (string value)
658                 {
659                         return this == value;
660                 }
661
662                 public static bool Equals (string a, string b)
663                 {
664                         return a == b;
665                 }
666
667                 public static string Format (string format, object arg0) {
668                         return Format (null, format, new object[] { arg0 });
669                 }
670
671                 public static string Format (string format, object arg0, object arg1) {
672                         return Format (null, format, new object[] { arg0, arg1 });
673                 }
674
675                 public static string Format (string format, object arg0, object arg1, object arg2) {
676                         return Format (null, format, new object[] { arg0, arg1, arg2 });
677                 }
678
679                 public static string Format (string format, params object[] args) {
680                         return Format (null, format, args);
681                 }
682
683                 public static string Format (IFormatProvider provider, string format, params object[] args) {
684                         if (format == null || args == null)
685                                 throw new ArgumentNullException ();
686                 
687                         StringBuilder result = new StringBuilder ();
688
689                         int ptr = 0;
690                         int start = ptr;
691                         while (ptr < format.Length) {
692                                 char c = format[ptr ++];
693
694                                 if (c == '{') {
695                                         result.Append (format, start, ptr - start - 1);
696
697                                         // check for escaped open bracket
698
699                                         if (format[ptr] == '{') {
700                                                 start = ptr ++;
701                                                 continue;
702                                         }
703
704                                         // parse specifier
705                                 
706                                         int n, width;
707                                         bool left_align;
708                                         string arg_format;
709
710                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
711                                         if (n >= args.Length)
712                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
713
714                                         // format argument
715
716                                         object arg = args[n];
717
718                                         string str;
719                                         if (arg == null)
720                                                 str = "";
721                                         else if (arg is IFormattable)
722                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
723                                         else
724                                                 str = arg.ToString ();
725
726                                         // pad formatted string and append to result
727
728                                         if (width > str.Length) {
729                                                 string pad = new String (' ', width - str.Length);
730
731                                                 if (left_align) {
732                                                         result.Append (str);
733                                                         result.Append (pad);
734                                                 }
735                                                 else {
736                                                         result.Append (pad);
737                                                         result.Append (str);
738                                                 }
739                                         }
740                                         else
741                                                 result.Append (str);
742
743                                         start = ptr;
744                                 }
745                                 else if (c == '}' && format[ptr] == '}') {
746                                         result.Append (format, start, ptr - start - 1);
747                                         start = ptr ++;
748                                 }
749                         }
750
751                         if (start < format.Length)
752                                 result.Append (format.Substring (start));
753
754                         return result.ToString ();
755                 }
756
757                 public CharEnumerator GetEnumerator ()
758                 {
759                         return new CharEnumerator (this);
760                 }
761                 
762                 IEnumerator IEnumerable.GetEnumerator ()
763                 {
764                         return new CharEnumerator (this);
765                 }
766
767                 public override int GetHashCode ()
768                 {
769                         int h = 0;
770                         int i;
771                         for (i = 0; i < length; ++i)
772                                 h = (h << 5) - h + c_str [i];
773                         return h;
774                 }
775
776                 public TypeCode GetTypeCode ()
777                 {
778                         return TypeCode.String;
779                 }
780
781                 public int IndexOf (char value)
782                 {
783                         return IndexOf (value, 0, this.length);
784                 }
785
786                 public int IndexOf (string value)
787                 {
788                         return IndexOf (value, 0, this.length);
789                 }
790
791                 public int IndexOf (char value, int startIndex)
792                 {
793                         return IndexOf (value, startIndex, this.length - startIndex);
794                 }
795
796                 public int IndexOf (string value, int startIndex)
797                 {
798                         return IndexOf (value, startIndex, this.length - startIndex);
799                 }
800
801                 public int IndexOf (char value, int startIndex, int count)
802                 {
803                         int i;
804
805                         if (startIndex < 0 || count < 0 || startIndex + count > this.length)
806                                 throw new ArgumentOutOfRangeException ();
807
808                         for (i = startIndex; i - startIndex < count; i++)
809                                 if (this.c_str[i] == value)
810                                         return i;
811
812                         return -1;
813                 }
814
815                 public int IndexOf (string value, int startIndex, int count)
816                 {
817                         if (value == null)
818                                 throw new ArgumentNullException ();
819
820                         if (startIndex < 0 || count < 0 || startIndex + count > this.length)
821                                 throw new ArgumentOutOfRangeException ();
822
823 #if XXX
824                         return BoyerMoore (this.c_str, value, startIndex, count);
825 #endif
826                         int i;
827                         for (i = startIndex; i - startIndex + value.length <= count; ) {
828                                 if (this.c_str[i] == value.c_str [0]) {
829                                         bool equal = true;
830                                         int j, nexti = 0;
831
832                                         for (j = 1; equal && j < value.length; j++) {
833                                                 equal = this.c_str[i + j] == value.c_str [j];
834                                                 if (this.c_str [i + j] == value.c_str [0] && nexti == 0)
835                                                         nexti = i + j;
836                                         }
837
838                                         if (equal)
839                                                 return i;
840
841                                         if (nexti != 0)
842                                                 i = nexti;
843                                         else
844                                                 i += j;
845                                 } else
846                                         i++;
847                         }
848
849                         return -1;
850                 }
851
852                 public int IndexOfAny (char[] values)
853                 {
854                         return IndexOfAny (values, 0, this.length);
855                 }
856
857                 public int IndexOfAny (char[] values, int startIndex)
858                 {
859                         return IndexOfAny (values, startIndex, this.length - startIndex);
860                 }
861
862                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
863                 internal extern int InternalIndexOfAny (char[] values, int startIndex, int count);
864
865                 public int IndexOfAny (char[] values, int startIndex, int count)
866                 {
867                         if (values == null)
868                                 throw new ArgumentNullException ();
869
870                         if (startIndex < 0 || count < 0 || startIndex + count > this.length)
871                                 throw new ArgumentOutOfRangeException ();
872
873                         return InternalIndexOfAny (values, startIndex, count);
874                 }
875
876                 public string Insert (int startIndex, string value)
877                 {
878                         char[] str;
879                         int i, j;
880
881                         if (value == null)
882                                 throw new ArgumentNullException ();
883
884                         if (startIndex < 0 || startIndex > this.length)
885                                 throw new ArgumentOutOfRangeException ();
886
887                         String res = new String (value.length + this.length);
888
889                         str = res.c_str;
890                         for (i = 0; i < startIndex; i++)
891                                 str[i] = this.c_str[i];
892                         for (j = 0; j < value.length; j++)
893                                 str[i + j] = value.c_str[j];
894                         for ( ; i < this.length; i++)
895                                 str[i + j] = this.c_str[i];
896
897                         return res;
898                 }
899
900                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
901                 internal extern static string _Intern (string str);
902
903                 public static string Intern (string str)
904                 {
905                         if (str == null)
906                                 throw new ArgumentNullException ();
907
908                         return _Intern (str);
909                 }
910
911                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
912                 internal extern static string _IsInterned (string str);
913
914                 public static string IsInterned (string str)
915                 {
916                         if (str == null)
917                                 throw new ArgumentNullException ();
918
919                         return _IsInterned (str);
920                 }
921
922                 public static string Join (string separator, string[] value)
923                 {
924                         if (value == null)
925                                 throw new ArgumentNullException ();
926
927                         return Join (separator, value, 0, value.Length);
928                 }
929
930                 public static string Join (string separator, string[] value, int startIndex, int count)
931                 {
932                         // LAMESPEC: msdn doesn't specify what happens when separator is null
933                         int len, i, j, used;
934                         char[] str;
935
936                         if (separator == null || value == null)
937                                 throw new ArgumentNullException ();
938
939                         if (startIndex + count > value.Length)
940                                 throw new ArgumentOutOfRangeException ();
941
942                         len = 0;
943                         for (i = startIndex, used = 0; used < count; i++, used++) {
944                                 if (i != startIndex)
945                                         len += separator.length;
946
947                                 len += value[i].length;
948                         }
949
950                         // We have no elements to join?
951                         if (i == startIndex)
952                                 return String.Empty;
953
954                         String res = new String (len);
955
956                         str = res.c_str;
957                         for (i = 0; i < value[startIndex].length; i++)
958                                 str[i] = value[startIndex][i];
959
960                         used = 1;
961                         for (j = startIndex + 1; used < count; j++, used++) {
962                                 int k;
963
964                                 for (k = 0; k < separator.length; k++)
965                                         str[i++] = separator.c_str[k];
966                                 for (k = 0; k < value[j].length; k++)
967                                         str[i++] = value[j].c_str[k];
968                         }
969
970                         return res;
971                 }
972
973                 public int LastIndexOf (char value)
974                 {
975                         int i = this.length;
976                         if (i == 0)
977                                 return -1;
978                         --i;
979                         for (; i >= 0; i--) {
980                                 if (this.c_str[i] == value)
981                                         return i;
982                         }
983
984                         return -1;
985                 }
986
987                 public int LastIndexOf (string value)
988                 {
989                         if (value == null)
990                                 throw new ArgumentNullException ();
991                         if (value.length == 0)
992                                 return 0;
993                         if (this.length == 0)
994                                 return -1;
995                                 
996                         return LastIndexOf (value, this.length - 1, this.length);
997                 }
998
999                 public int LastIndexOf (char value, int startIndex)
1000                 {
1001                         if (startIndex < 0 || startIndex >= this.length)
1002                                 throw new ArgumentOutOfRangeException ();
1003
1004                         for (int i = startIndex; i >= 0; i--) {
1005                                 if (this.c_str[i] == value)
1006                                         return i;
1007                         }
1008
1009                         return -1;
1010                 }
1011
1012                 public int LastIndexOf (string value, int startIndex)
1013                 {
1014                         return LastIndexOf (value, startIndex, startIndex + 1);
1015                 }
1016
1017                 public int LastIndexOf (char value, int startIndex, int count)
1018                 {
1019                         if (startIndex < 0 || count < 0)
1020                                 throw new ArgumentOutOfRangeException ();
1021
1022                         if (startIndex >= this.length || startIndex - count + 1 < 0)
1023                                 throw new ArgumentOutOfRangeException ();
1024
1025                         for (int i = startIndex; i > startIndex - count; i--) {
1026                                 if (this.c_str[i] == value)
1027                                         return i;
1028                         }
1029
1030                         return -1;
1031                 }
1032
1033                 public int LastIndexOf (string value, int startIndex, int count)
1034                 {
1035                         // startIndex points to the end of value, ie. we're searching backwards.
1036                         int i, len;
1037
1038                         if (value == null)
1039                                 throw new ArgumentNullException ();
1040
1041                         if (startIndex < 0 || startIndex > this.length)
1042                                 throw new ArgumentOutOfRangeException ();
1043
1044                         if (count < 0 || startIndex - count + 1 < 0)
1045                                 throw new ArgumentOutOfRangeException ();
1046
1047                         if (value.length > startIndex)
1048                                 return -1;
1049
1050                         if (value == String.Empty)
1051                                 return startIndex;
1052
1053                         if (startIndex == this.length)
1054                                 startIndex--;
1055
1056                         // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1057                         len = value.length - 1;
1058                         for (i = startIndex; i > startIndex - count; i--) {
1059
1060                                 if (this.c_str[i] == value.c_str[len]) {
1061                                         bool equal = true;
1062                                         int j;
1063
1064                                         for (j = 0; equal && j < len; j++)
1065                                                 equal = this.c_str[i - j] == value.c_str[len - j];
1066
1067                                         if (equal)
1068                                                 return i - j;
1069                                 }
1070                         }
1071
1072                         return -1;
1073                 }
1074
1075                 public int LastIndexOfAny (char[] values)
1076                 {
1077                         return LastIndexOfAny (values, this.length - 1, this.length);
1078                 }
1079
1080                 public int LastIndexOfAny (char[] values, int startIndex)
1081                 {
1082                         return LastIndexOfAny (values, startIndex, startIndex + 1);
1083                 }
1084
1085                 public int LastIndexOfAny (char[] values, int startIndex, int count)
1086                 {
1087                         int i;
1088
1089                         if (values == null)
1090                                 throw new ArgumentNullException ();
1091
1092                         if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1093                                 throw new ArgumentOutOfRangeException ();
1094
1095                         for (i = startIndex; i > startIndex - count; i--) {
1096                                 for (int j = 0; j < strlen (values); j++) {
1097                                         if (this.c_str[i] == values[j])
1098                                                 return i;
1099                                 }
1100                         }
1101
1102                         return -1;
1103                 }
1104
1105                 public string PadLeft (int totalWidth)
1106                 {
1107                         return PadLeft (totalWidth, ' ');
1108                 }
1109
1110                 public string PadLeft (int totalWidth, char padChar)
1111                 {
1112                         char[] str;
1113                         int i, j;
1114
1115                         if (totalWidth < 0)
1116                                 throw new ArgumentException ();
1117
1118                         str = new char [totalWidth > this.length ? totalWidth : this.length];
1119                         for (i = 0; i < totalWidth - this.length; i++)
1120                                 str[i] = padChar;
1121
1122                         for (j = 0; j < this.length; i++, j++)
1123                                 str[i] = this.c_str[j];
1124
1125                         return new String (str);
1126                 }
1127
1128                 public string PadRight (int totalWidth)
1129                 {
1130                         return PadRight (totalWidth, ' ');
1131                 }
1132
1133                 public string PadRight (int totalWidth, char padChar)
1134                 {
1135                         char[] str;
1136                         int i;
1137
1138                         if (totalWidth < 0)
1139                                 throw new ArgumentException ();
1140
1141                         str = new char [totalWidth > this.length ? totalWidth : this.length];
1142                         for (i = 0; i < this.length; i++)
1143                                 str[i] = this.c_str[i];
1144
1145                         for ( ; i < str.Length; i++)
1146                                 str[i] = padChar;
1147
1148                         return new String (str);
1149                 }
1150
1151                 public string Remove (int startIndex, int count)
1152                 {
1153                         char[] str;
1154                         int i, j, len;
1155
1156                         if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1157                                 throw new ArgumentOutOfRangeException ();
1158
1159                         len = this.length - count;
1160                         if (len == 0)
1161                                 return String.Empty;
1162                         
1163                         String res = new String (len);
1164                         str = res.c_str;
1165                         for (i = 0; i < startIndex; i++)
1166                                 str[i] = this.c_str[i];
1167                         for (j = i + count; j < this.length; j++)
1168                                 str[i++] = this.c_str[j];
1169
1170                         return res;
1171                 }
1172
1173                 public string Replace (char oldChar, char newChar)
1174                 {
1175                         char[] str;
1176                         int i;
1177
1178                         String res = new String (length);
1179                         str = res.c_str;
1180                         for (i = 0; i < this.length; i++) {
1181                                 if (this.c_str[i] == oldChar)
1182                                         str[i] = newChar;
1183                                 else
1184                                         str[i] = this.c_str[i];
1185                         }
1186
1187                         return res;
1188                 }
1189
1190                 public string Replace (string oldValue, string newValue)
1191                 {
1192                         // If newValue is null, oldValue is removed.
1193                         int index, len, i, j, newlen;
1194                         string thestring;
1195                         char[] str;
1196
1197                         if (oldValue == null)
1198                                 throw new ArgumentNullException ();
1199
1200                         thestring = Substring (0, this.length);
1201                         index = 0;
1202
1203                         // Runs until all occurences of oldValue have been replaced.
1204                         while (true) {
1205                                 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1206                                 index = thestring.IndexOf (oldValue, index);
1207
1208                                 if (index == -1)
1209                                         return thestring;
1210
1211                                 newlen = (newValue == null) ? 0 : newValue.length;
1212                                 len = thestring.length - oldValue.length + newlen;
1213
1214                                 if (len == 0)
1215                                         return String.Empty;
1216
1217                                 String res = new String (len);
1218                                 str = res.c_str;
1219                                 for (i = 0; i < index; i++)
1220                                         str[i] = thestring.c_str[i];
1221                                 for (j = 0; j < newlen; j++)
1222                                         str[i++] = newValue[j];
1223                                 for (j = index + oldValue.length; j < thestring.length; j++)
1224                                         str[i++] = thestring.c_str[j];
1225
1226                                 // Increment index, we're already done replacing until this index.
1227                                 thestring = res;
1228                                 index += newlen;
1229                         }
1230                 }
1231
1232                 private int splitme (char[] separators, int startIndex)
1233                 {
1234                         /* this is basically a customized IndexOfAny() for the Split() methods */
1235                         for (int i = startIndex; i < this.length; i++) {
1236                                 if (separators != null) {
1237                                         foreach (char sep in separators) {
1238                                                 if (this.c_str[i] == sep)
1239                                                         return i - startIndex;
1240                                         }
1241                                 } else if (is_lwsp (this.c_str[i])) {
1242                                         return i - startIndex;
1243                                 }
1244                         }
1245
1246                         return -1;
1247                 }
1248
1249                 public string[] Split (params char[] separator)
1250                 {
1251                         /**
1252                          * split:
1253                          * @separator: delimiting chars or null to split on whtspc
1254                          *
1255                          * Returns: 1. An array consisting of a single
1256                          * element (@this) if none of the delimiting
1257                          * chars appear in @this. 2. An array of
1258                          * substrings which are delimited by one of
1259                          * the separator chars. 3. An array of
1260                          * substrings separated by whitespace if
1261                          * @separator is null. The Empty string should
1262                          * be returned wherever 2 delimiting chars are
1263                          * adjacent.
1264                          **/
1265                         // FIXME: would using a Queue be better?
1266                         string[] strings;
1267                         ArrayList list;
1268                         int index, len;
1269
1270                         list = new ArrayList ();
1271                         for (index = 0, len = 0; index < this.length; index += len + 1) {
1272                                 len = splitme (separator, index);
1273                                 len = len > -1 ? len : this.length - index;
1274                                 if (len == 0) {
1275                                         list.Add (String.Empty);
1276                                 } else {
1277                                         char[] str;
1278                                         int i;
1279
1280                                         str = new char [len];
1281                                         for (i = 0; i < len; i++)
1282                                                 str[i] = this.c_str[index + i];
1283
1284                                         list.Add (new String (str));
1285                                 }
1286                         }
1287
1288                         strings = new string [list.Count];
1289                         if (list.Count == 1) {
1290                                 /* special case for an array holding @this */
1291                                 strings[0] = this;
1292                         } else {
1293                                 for (index = 0; index < list.Count; index++)
1294                                         strings[index] = (string) list[index];
1295                         }
1296
1297                         return strings;
1298                 }
1299
1300                 public string[] Split (char[] separator, int maxCount)
1301                 {
1302                         // FIXME: would using Queue be better than ArrayList?
1303                         string[] strings;
1304                         ArrayList list;
1305                         int index, len, used;
1306
1307                         if (maxCount == 0)
1308                                 return new string[0];
1309                         else if (maxCount < 0)
1310                                 throw new ArgumentOutOfRangeException ();
1311
1312                         used = 0;
1313                         list = new ArrayList ();
1314                         for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1315                                 len = splitme (separator, index);
1316                                 len = len > -1 ? len : this.length - index;
1317                                 if (len == 0) {
1318                                         list.Add (String.Empty);
1319                                 } else {
1320                                         char[] str;
1321                                         int i;
1322
1323                                         str = new char [len];
1324                                         for (i = 0; i < len; i++)
1325                                                 str[i] = this.c_str[index + i];
1326
1327                                         list.Add (new String (str));
1328                                 }
1329                                 used++;
1330                         }
1331
1332                         /* fit the remaining chunk of the @this into it's own element */
1333                         if (index <= this.length)
1334                         {
1335                                 char[] str;
1336                                 int i;
1337
1338                                 str = new char [this.length - index];
1339                                 for (i = index; i < this.length; i++)
1340                                         str[i - index] = this.c_str[i];
1341
1342                                 // maxCount cannot be zero if we reach this point and this means that
1343                                 // index can't be zero either.
1344                                 if (used == maxCount)
1345                                         list[used-1] += this.c_str[index-1] + new String (str);
1346                                 else
1347                                 list.Add (new String (str));
1348                         }
1349
1350                         strings = new string [list.Count];
1351                         if (list.Count == 1) {
1352                                 /* special case for an array holding @this */
1353                                 strings[0] = this;
1354                         } else {
1355                                 for (index = 0; index < list.Count; index++)
1356                                         strings[index] = (string) list[index];
1357                         }
1358
1359                         return strings;
1360                 }
1361
1362                 public bool StartsWith (string value)
1363                 {
1364                         bool startswith = true;
1365                         int i;
1366
1367                         if (value == null)
1368                                 throw new ArgumentNullException ();
1369
1370                         if (value.length > this.length)
1371                                 return false;
1372
1373                         for (i = 0; i < value.length && startswith; i++)
1374                                 startswith = startswith && value.c_str[i] == this.c_str[i];
1375
1376                         return startswith;
1377                 }
1378
1379                 public string Substring (int startIndex)
1380                 {
1381                         char[] str;
1382                         int i, len;
1383
1384                         if (startIndex < 0 || startIndex > this.length)
1385                                 throw new ArgumentOutOfRangeException ();
1386
1387                         len = this.length - startIndex;
1388                         if (len == 0)
1389                                 return String.Empty;
1390                         String res = new String (len);
1391                         str = res.c_str;
1392                         for (i = startIndex; i < this.length; i++)
1393                                 str[i - startIndex] = this.c_str[i];
1394
1395                         return res;
1396                 }
1397
1398                 public string Substring (int startIndex, int length)
1399                 {
1400                         char[] str;
1401                         int i;
1402
1403                         if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1404                                 throw new ArgumentOutOfRangeException ();
1405
1406                         if (length == 0)
1407                                 return String.Empty;
1408                         String res = new String (length);
1409                         str = res.c_str;
1410
1411                         // NOTE: Not all the characters are adjacent at the beginning of the
1412                         //      array.  StringBuilder.Append() will place characters at the end
1413                         //      of the array, potentially leaving null characters in between.
1414                         int CopyCount = 0;
1415                         for (i = startIndex; i < this.length && CopyCount < length; i++){
1416                                 // Don't copy null characters
1417                                 if ((int)this.c_str[i] != 0) {
1418                                         str[CopyCount] = this.c_str[i];
1419                                         CopyCount++;
1420                                 }
1421                         }
1422                         return res;
1423                 }
1424
1425                 bool IConvertible.ToBoolean (IFormatProvider provider)
1426                 {
1427                         return Convert.ToBoolean (this);
1428                 }
1429                 
1430                 byte IConvertible.ToByte (IFormatProvider provider)
1431                 {
1432                         return Convert.ToByte (this);
1433                 }
1434                 
1435                 char IConvertible.ToChar (IFormatProvider provider)
1436                 {
1437                         return Convert.ToChar (this);
1438                 }
1439
1440                 public char[] ToCharArray ()
1441                 {
1442                         return ToCharArray (0, this.length);
1443                 }
1444
1445                 public char[] ToCharArray (int startIndex, int length)
1446                 {
1447                         char[] chars;
1448                         int i;
1449
1450                         if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1451                                 throw new ArgumentOutOfRangeException ();
1452
1453                         chars = new char [length];
1454                         for (i = startIndex; i < length; i++)
1455                                 chars[i - startIndex] = this.c_str[i];
1456
1457                         return chars;
1458                 }
1459
1460                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1461                 {
1462                         return Convert.ToDateTime (this);
1463                 }
1464
1465                 decimal IConvertible.ToDecimal (IFormatProvider provider)
1466                 {
1467                         return Convert.ToDecimal (this);
1468                 }
1469
1470                 double IConvertible.ToDouble (IFormatProvider provider)
1471                 {
1472                         return Convert.ToDouble (this);
1473                 }
1474
1475                 short IConvertible.ToInt16 (IFormatProvider provider)
1476                 {
1477                         return Convert.ToInt16 (this);
1478                 }
1479
1480                 int IConvertible.ToInt32 (IFormatProvider provider)
1481                 {
1482                         return Convert.ToInt32 (this);
1483                 }
1484
1485                 long IConvertible.ToInt64 (IFormatProvider provider)
1486                 {
1487                         return Convert.ToInt64 (this);
1488                 }
1489
1490                 public string ToLower ()
1491                 {
1492                         char[] str;
1493                         int i;
1494
1495                         String res = new String (length);
1496                         str = res.c_str;
1497                         for (i = 0; i < this.length; i++)
1498                                 str[i] = Char.ToLower (this.c_str[i]);
1499
1500                         return res;
1501                 }
1502
1503                 [MonoTODO]
1504                 public string ToLower (CultureInfo culture)
1505                 {
1506                         // FIXME: implement me
1507                         throw new NotImplementedException ();
1508
1509                 }
1510
1511                 [CLSCompliant(false)]
1512                 sbyte IConvertible.ToSByte (IFormatProvider provider)
1513                 {
1514                         return Convert.ToSByte (this);
1515                 }
1516
1517                 float IConvertible.ToSingle (IFormatProvider provider)
1518                 {
1519                         return Convert.ToSingle (this);
1520                 }
1521
1522                 public override string ToString ()
1523                 {
1524                         return this;
1525                 }
1526
1527                 string IConvertible.ToString (IFormatProvider format)
1528                 {
1529                         return this;
1530                 }
1531
1532                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1533                 {
1534                         return Convert.ToType (this, conversionType,  provider);
1535                 }
1536
1537                 [CLSCompliant(false)]
1538                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1539                 {
1540                         return Convert.ToUInt16 (this);
1541                 }
1542
1543                 [CLSCompliant(false)]
1544                 uint IConvertible.ToUInt32 (IFormatProvider provider)
1545                 {
1546                         return Convert.ToUInt32 (this);
1547                 }
1548
1549                 [CLSCompliant(false)]
1550                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1551                 {
1552                         return Convert.ToUInt64 (this);
1553                 }
1554
1555                 public string ToUpper ()
1556                 {
1557                         char[] str;
1558                         int i;
1559
1560                         String res = new String (length);
1561                         str = res.c_str;
1562                         for (i = 0; i < this.length; i++)
1563                                 str[i] = Char.ToUpper (this.c_str[i]);
1564
1565                         return res;
1566                 }
1567
1568                 [MonoTODO]
1569                 public string ToUpper (CultureInfo culture)
1570                 {
1571                         // FIXME: implement me
1572                         throw new NotImplementedException ();
1573                 }
1574
1575                 public string Trim ()
1576                 {
1577                         return Trim (null);
1578                 }
1579
1580                 public string Trim (params char[] trimChars)
1581                 {
1582                         int begin, end;
1583                         bool matches = false;
1584
1585                         for (begin = 0; begin < this.length; begin++) {
1586                                 if (trimChars != null) {
1587                                         matches = false;
1588                                         foreach (char c in trimChars) {
1589                                                 matches = this.c_str[begin] == c;
1590                                                 if (matches)
1591                                                         break;
1592                                         }
1593                                         if (matches)
1594                                                 continue;
1595                                 } else {
1596                                         matches = is_lwsp (this.c_str[begin]);
1597                                         if (matches)
1598                                                 continue;
1599                                 }
1600                                 break;
1601                         }
1602
1603                         for (end = this.length - 1; end > begin; end--) {
1604                                 if (trimChars != null) {
1605                                         matches = false;
1606                                         foreach (char c in trimChars) {
1607                                                 matches = this.c_str[end] == c;
1608                                                 if (matches)
1609                                                         break;
1610                                         }
1611                                         if (matches)
1612                                                 continue;
1613                                 } else {
1614                                         matches = is_lwsp (this.c_str[end]);
1615                                         if (matches)
1616                                                 continue;
1617                                 }
1618                                 break;
1619                         }
1620                         end++;
1621
1622                         if (begin >= end)
1623                                 return String.Empty;
1624
1625                         return Substring (begin, end - begin);
1626                 }
1627
1628                 public string TrimEnd (params char[] trimChars)
1629                 {
1630                         bool matches = true;
1631                         int end;
1632
1633                         for (end = this.length - 1; matches && end > 0; end--) {
1634
1635                                 if (trimChars != null) {
1636                                         matches = false;
1637                                         foreach (char c in trimChars) {
1638                                                 matches = this.c_str[end] == c;
1639                                                 if (matches)
1640                                                         break;
1641                                         }
1642                                 } else {
1643                                         matches = is_lwsp (this.c_str[end]);
1644                                 }
1645
1646                                 if (!matches)
1647                                         return Substring (0, end+1);
1648                         }
1649
1650                         if (end == 0)
1651                                 return String.Empty;
1652
1653                         return Substring (0, end);
1654                 }
1655
1656                 public string TrimStart (params char[] trimChars)
1657                 {
1658                         bool matches = true;
1659                         int begin;
1660
1661                         for (begin = 0; matches && begin < this.length; begin++) {
1662                                 if (trimChars != null) {
1663                                         matches = false;
1664                                         foreach (char c in trimChars) {
1665                                                 matches = this.c_str[begin] == c;
1666                                                 if (matches)
1667                                                         break;
1668                                         }
1669                                 } else {
1670                                         matches = is_lwsp (this.c_str[begin]);
1671                                 }
1672
1673                                 if (!matches)
1674                                         return Substring (begin, this.length - begin);
1675                         }
1676
1677                                 return String.Empty;
1678                 }
1679
1680                 // Operators
1681                 public static bool operator ==(string a, string b)
1682                 {
1683                         if ((object)a == null) {
1684                                 if ((object)b == null)
1685                                         return true;
1686                                 return false;
1687                         }
1688                         if ((object)b == null)
1689                                 return false;
1690         
1691                         if (a.length != b.length)
1692                                 return false;
1693
1694                         int l = a.length;
1695                         for (int i = 0; i < l; i++)
1696                                 if (a.c_str[i] != b.c_str[i])
1697                                         return false;
1698
1699                         return true;
1700                 }
1701
1702                 public static bool operator !=(string a, string b)
1703                 {
1704                         return !(a == b);
1705                 }
1706
1707                 // private
1708
1709                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1710                         // parses format specifier of form:
1711                         //   N,[[-]M][:F]}
1712                         //
1713                         // where:
1714
1715                         try {
1716                                 // N = argument number (non-negative integer)
1717                         
1718                                 n = ParseDecimal (str, ref ptr);
1719                                 if (n < 0)
1720                                         throw new FormatException ("Input string was not in correct format.");
1721                                 
1722                                 // M = width (non-negative integer)
1723
1724                                 if (str[ptr] == ',') {
1725                                         left_align = (str[++ ptr] == '-');
1726                                         if (left_align)
1727                                                 ++ ptr;
1728
1729                                         width = ParseDecimal (str, ref ptr);
1730                                         if (width < 0)
1731                                                 throw new FormatException ("Input string was not in correct format.");
1732                                 }
1733                                 else {
1734                                         width = 0;
1735                                         left_align = false;
1736                                 }
1737
1738                                 // F = argument format (string)
1739
1740                                 if (str[ptr] == ':') {
1741                                         int start = ++ ptr;
1742                                         while (str[ptr] != '}')
1743                                                 ++ ptr;
1744
1745                                         format = str.Substring (start, ptr - start);
1746                                 }
1747                                 else
1748                                         format = null;
1749
1750                                 if (str[ptr ++] != '}')
1751                                         throw new FormatException ("Input string was not in correct format.");
1752                         }
1753                         catch (IndexOutOfRangeException) {
1754                                 throw new FormatException ("Input string was not in correct format.");
1755                         }
1756                 }
1757
1758                 private static int ParseDecimal (string str, ref int ptr) {
1759                         int p = ptr;
1760                         int n = 0;
1761                         while (true) {
1762                                 char c = str[p];
1763                                 if (c < '0' || '9' < c)
1764                                         break;
1765
1766                                 n = n * 10 + c - '0';
1767                                 ++ p;
1768                         }
1769
1770                         if (p == ptr)
1771                                 return -1;
1772                         
1773                         ptr = p;
1774                         return n;
1775                 }
1776         }
1777 }