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