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