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