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