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