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