2003-12-06 Dick Porter <dick@ximian.com>
[mono.git] / mcs / class / corlib / System / String.cs
1 //
2 // System.String.cs
3 //
4 // Authors:
5 //        Patrik Torstensson (patrik.torstensson@labs2.com)
6 //   Jeffrey Stedfast (fejj@ximian.com)
7 //   Dan Lewis (dihlewis@yahoo.co.uk)
8 //
9 // (C) 2001 Ximian, Inc.  http://www.ximian.com
10 //
11
12 using System;
13 using System.Text;
14 using System.Collections;
15 using System.Globalization;
16 using System.Runtime.CompilerServices;
17
18 namespace System {
19         [Serializable]
20         public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable {
21                 [NonSerialized]
22                 private int length;
23
24                 private const int COMPARE_CASE = 0;
25                 private const int COMPARE_INCASE = 1;
26                 private const int COMPARE_ORDINAL = 2;
27
28                 public static readonly String Empty = "";
29
30                 public static bool Equals(String str1, String str2) {
31                         if ((str1 as object) == (str2 as object))
32                                 return true;
33             
34                         if (null == str1 || null == str2)
35                                 return false;
36
37                         int len = str1.length;
38                         
39                         if (len != str2.length)
40                                 return false;
41
42                         for (int i = 0; i < len; i++)
43                                 if (str1 [i] != str2 [i])
44                                         return false;
45
46                         return true;
47                 }
48
49                 public static bool operator == (String str1, String str2) {
50                         return Equals(str1, str2);
51                 }
52
53                 public static bool operator != (String str1, String str2) {
54                         return !Equals(str1, str2);
55                 }
56
57                 public override bool Equals(Object obj) {
58                         return Equals (this, obj as String);
59                 }
60
61                 public bool Equals(String value) {
62                         return Equals (this, value);
63                 }
64
65                 [IndexerName("Chars")]
66                 public extern char this[int index] {
67                         [MethodImplAttribute(MethodImplOptions.InternalCall)]
68                         get;
69                 }
70
71                 public Object Clone() {
72                         return this;
73                 }
74
75                 public TypeCode GetTypeCode () {
76                         return TypeCode.String;
77                 }
78
79                 public void CopyTo(int sindex, char[] dest, int dindex, int count) {
80                         // LAMESPEC: should I null-terminate?
81                         
82                         if (dest == null)
83                                 throw new ArgumentNullException();
84
85                         if (sindex < 0 || dindex < 0 || count < 0)
86                                 throw new ArgumentOutOfRangeException (); 
87
88                         if (sindex + count > Length)
89                                 throw new ArgumentOutOfRangeException ();
90
91                         if (dindex + count > dest.Length)
92                                 throw new ArgumentOutOfRangeException ();
93
94                         InternalCopyTo(sindex, dest, dindex, count);
95                 }
96
97                 public char[] ToCharArray() {
98                         return ToCharArray(0, length);
99                 }
100
101                 public char[] ToCharArray(int sindex, int length) {
102                         if (sindex < 0 || length < 0 || sindex + length > this.length)
103                                 throw new ArgumentOutOfRangeException (); 
104
105                         char [] tmp = new char[length];
106
107                         InternalCopyTo(sindex, tmp, 0, length);
108
109                         return tmp;
110                 }
111                 
112                 public String [] Split(params char [] separator) {
113                         return Split(separator, Int32.MaxValue);
114                 }
115
116                 public String[] Split(char[] separator, int count) {
117                         if (null == separator || separator.Length == 0) {
118                                 separator = WhiteChars;
119                         }
120
121                         if (count < 0)
122                                 throw new ArgumentOutOfRangeException ();
123
124                         if (count == 0) 
125                                 return new String[0];
126
127                         if (count == 1) 
128                                 return new String[1] { ToString() };
129
130                         return InternalSplit(separator, count);
131                 }
132
133                 public String Substring (int sindex) {
134                         if (sindex < 0 || sindex > this.length) {
135                                 throw new ArgumentOutOfRangeException();
136                         }
137
138                         string tmp = InternalAllocateStr(this.length - sindex);
139                         InternalStrcpy(tmp, 0, this, sindex, length - sindex);
140                         
141                         return tmp;
142                 }
143
144                 public String Substring (int sindex, int length) {
145                         if (length < 0 || sindex < 0 || sindex + length > this.length) {
146                                 throw new ArgumentOutOfRangeException();
147                         }
148
149                         if (length == 0)
150                                 return String.Empty;
151
152                         string tmp = InternalAllocateStr(length);
153                         InternalStrcpy(tmp, 0, this, sindex, length);
154
155                         return tmp;
156                 }       
157
158                 private static readonly char[] WhiteChars = {  (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD, (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004, (char) 0x2005,
159                                                                                                                                           (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B, (char) 0x3000, (char) 0xFEFF };
160
161                 public String Trim(params char[] chars) {
162                         if (null == chars || chars.Length == 0)
163                                 chars = WhiteChars;
164
165                         return InternalTrim(chars, 0);
166                 }
167
168                 public String TrimStart(params char[] chars) {
169                         if (null == chars || chars.Length == 0)
170                                 chars = WhiteChars;
171
172                         return InternalTrim(chars, 1);
173                 }
174
175                 public String TrimEnd(params char[] chars) {
176                         if (null == chars || chars.Length == 0)
177                                 chars = WhiteChars;
178
179                         return InternalTrim(chars, 2);
180                 }
181
182                 public static int Compare(String s1, String s2) {
183                         return(Compare(s1, s2, false,
184                                        CultureInfo.CurrentCulture));
185                 }
186
187                 public static int Compare(String s1, String s2, bool inCase) {
188                         return(Compare (s1, s2, inCase,
189                                         CultureInfo.CurrentCulture));
190                 }
191                 
192                 public static int Compare(String s1, String s2, bool inCase,
193                                           CultureInfo culture) {
194                         if (culture == null) {
195                                 throw new ArgumentNullException ("culture");
196                         }
197                         
198                         if (s1 == null) {
199                                 if (s2 == null) {
200                                         return(0);
201                                 } else {
202                                         return(-1);
203                                 }
204                         } else if (s2 == null) {
205                                 return(1);
206                         }
207
208                         CompareOptions compopts;
209                         
210                         if(inCase) {
211                                 compopts=CompareOptions.IgnoreCase;
212                         } else {
213                                 compopts=CompareOptions.None;
214                         }
215                         
216                         return(culture.CompareInfo.Compare (s1, s2, compopts));
217                 }
218
219                 public static int Compare(String s1, int i1, String s2, int i2,
220                                           int length) {
221                         return(Compare(s1, i1, s2, i2, length, false,
222                                        CultureInfo.CurrentCulture));
223                 }
224
225                 public static int Compare(String s1, int i1, String s2, int i2,
226                                           int length, bool inCase) {
227                         return(Compare (s1, i1, s2, i2, length, inCase,
228                                         CultureInfo.CurrentCulture));
229                 }
230                 
231                 public static int Compare(String s1, int i1, String s2, int i2,
232                                           int length, bool inCase,
233                                           CultureInfo culture) {
234                         if(culture==null) {
235                                 throw new ArgumentNullException ("culture");
236                         }
237
238                         if((i1 > s1.Length) ||
239                            (i2 > s2.Length) ||
240                            (i1 < 0) || (i2 < 0) || (length < 0)) {
241                                 throw new ArgumentOutOfRangeException ();
242                         }
243                         
244                         if (s1 == null) {
245                                 if (s2 == null) {
246                                         return(0);
247                                 } else {
248                                         return(-1);
249                                 }
250                         } else if (s2 == null) {
251                                 return(1);
252                         }
253                         
254                         CompareOptions compopts;
255                         
256                         if(inCase) {
257                                 compopts=CompareOptions.IgnoreCase;
258                         } else {
259                                 compopts=CompareOptions.None;
260                         }
261                         
262                         /* Need to cap the requested length to the
263                          * length of the string, because
264                          * CompareInfo.Compare will insist that length
265                          * <= (string.Length - offset)
266                          */
267                         int len1=length;
268                         int len2=length;
269                         
270                         if(length > (s1.Length - i1)) {
271                                 len1=s1.Length - i1;
272                         }
273
274                         if(length > (s2.Length - i2)) {
275                                 len2=s2.Length - i2;
276                         }
277
278                         return(culture.CompareInfo.Compare(s1, i1, len1,
279                                                            s2, i2, len2,
280                                                            compopts));
281                 }
282
283                 public int CompareTo(Object value) {
284                         if (null == value)
285                                 return 1;
286             
287                         if (!(value is String))
288                                 throw new ArgumentException();
289
290                         return String.Compare(this, (String) value, false);
291                 }
292
293                 public int CompareTo(String str) {
294                         if (null == str)
295                                 return 1;
296
297                         return Compare(this, str, false);
298                 }
299
300                 public static int CompareOrdinal(String s1, String s2) {
301                         if (s1 == null) {
302                                 if (s2 == null) {
303                                         return(0);
304                                 } else {
305                                         return(-1);
306                                 }
307                         } else if (s2 == null) {
308                                 return(1);
309                         }
310
311                         /* Invariant, because that is cheaper to
312                          * instantiate (and chances are it already has
313                          * been.)
314                          */
315                         return(CultureInfo.InvariantCulture.CompareInfo.Compare (s1, s2, CompareOptions.Ordinal));
316                 }
317
318                 public static int CompareOrdinal(String s1, int i1, String s2,
319                                                  int i2, int length)
320                 {
321                         if ((i1 > s1.Length) ||
322                             (i2 > s2.Length) ||
323                             (i1 < 0) || (i2 < 0) || (length < 0)) {
324                                 throw new ArgumentOutOfRangeException ();
325                         }
326
327                         if (s1 == null) {
328                                 if (s2 == null) {
329                                         return(0);
330                                 } else {
331                                         return(-1);
332                                 }
333                         } else if (s2 == null) {
334                                 return(1);
335                         }
336
337                         /* Need to cap the requested length to the
338                          * length of the string, because
339                          * CompareInfo.Compare will insist that length
340                          * <= (string.Length - offset)
341                          */
342                         int len1=length;
343                         int len2=length;
344                         
345                         if(length > (s1.Length - i1)) {
346                                 len1=s1.Length - i1;
347                         }
348
349                         if(length > (s2.Length - i2)) {
350                                 len2=s2.Length - i2;
351                         }
352
353                         return(CultureInfo.InvariantCulture.CompareInfo.Compare(s1, i1, len1, s2, i2, len2, CompareOptions.Ordinal));
354                 }
355
356                 public bool EndsWith(String value) {
357                         if (null == value)
358                                 throw new ArgumentNullException();
359
360                         if (value.length > this.length) {
361                                 return false;
362                         }
363
364                         return (0 == Compare(this, length - value.length, value, 0, value.length));
365                 }
366         
367                 public int IndexOfAny(char [] arr) {
368                         if (null == arr)
369                                 throw new ArgumentNullException();
370
371                         return InternalIndexOfAny(arr, 0, this.length);
372                 }
373
374                 public int IndexOfAny(char [] arr, int sindex) {
375                         if (null == arr)
376                                 throw new ArgumentNullException();
377                         if (sindex < 0 || sindex >= this.length)
378                                 throw new ArgumentOutOfRangeException();
379
380                         return InternalIndexOfAny(arr, sindex, this.length - sindex);
381                 }
382
383                 public int IndexOfAny(char [] arr, int sindex, int count) {
384                         if (null == arr)
385                                 throw new ArgumentNullException();
386                         if (sindex < 0 || count < 0 || sindex + count > this.length)
387                                 throw new ArgumentOutOfRangeException ();
388
389                         return InternalIndexOfAny(arr, sindex, count);
390                 }
391
392                 public int IndexOf(char value) {
393                         return(IndexOf(value, 0, this.length));
394                 }
395
396                 public int IndexOf(String value) {
397                         return(IndexOf(value, 0, this.length));
398                 }
399
400                 public int IndexOf(char value, int sindex) {
401                         return(IndexOf(value, sindex, this.length - sindex));
402                 }
403
404                 public int IndexOf(String value, int sindex) {
405                         return(IndexOf(value, sindex, this.length - sindex));
406                 }
407
408                 /* This method is culture-insensitive */
409                 public int IndexOf(char value, int sindex, int count) {
410                         if (sindex < 0 || count < 0 ||
411                             sindex + count > this.length) {
412                                 throw new ArgumentOutOfRangeException ();
413                         }
414
415                         if ((sindex == 0 && this.length == 0) ||
416                             (sindex == this.length) ||
417                             (count == 0)) {
418                                 return(-1);
419                         }
420                         
421                         for(int pos=sindex; pos < sindex + count; pos++) {
422                                 if(this[pos]==value) {
423                                         return(pos);
424                                 }
425                         }
426
427                         return(-1);
428                 }
429                 
430                 /* But this one is culture-sensitive */
431                 public int IndexOf(String value, int sindex, int count) {
432                         if (value == null) {
433                                 throw new ArgumentNullException();
434                         }
435
436                         if (sindex < 0 || count < 0 ||
437                             sindex + count > this.length) {
438                                 throw new ArgumentOutOfRangeException ();
439                         }
440
441                         if (value.length == 0) {
442                                 return sindex;
443                         }
444                         
445                         if (sindex == 0 && this.length == 0) {
446                                 return -1;
447                         }
448
449                         if (count == 0) {
450                                 return(-1);
451                         }
452                         
453                         return(CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, sindex, count));
454                 }
455
456                 public int LastIndexOfAny(char [] arr) {
457                         if (null == arr) 
458                                 throw new ArgumentNullException();
459
460                         return InternalLastIndexOfAny(arr, this.length - 1, this.length);
461                 }
462
463                 public int LastIndexOfAny(char [] arr, int sindex) {
464                         if (null == arr) 
465                                 throw new ArgumentNullException();
466
467                         if (sindex < 0 || sindex > this.length)
468                                 throw new ArgumentOutOfRangeException();
469
470                         if (this.length == 0)
471                                 return -1;
472
473                         return InternalLastIndexOfAny(arr, sindex, sindex + 1);
474                 }
475
476                 public int LastIndexOfAny(char [] arr, int sindex, int count) {
477                         if (null == arr) 
478                                 throw new ArgumentNullException();
479
480                         if (sindex < 0 || count < 0 || sindex > this.length || sindex - count < -1)
481                                 throw new ArgumentOutOfRangeException();
482
483                         if (this.length == 0)
484                                 return -1;
485
486                         return InternalLastIndexOfAny(arr, sindex, count);
487                 }
488
489                 public int LastIndexOf(char value) {
490                         if(this.length==0) {
491                                 return(-1);
492                         } else {
493                                 return(LastIndexOf(value, this.length - 1,
494                                                    this.length));
495                         }
496                         
497                 }
498
499                 public int LastIndexOf(String value) {
500                         if(this.length==0) {
501                                 /* This overload does additional checking */
502                                 return(LastIndexOf(value, 0, 0));
503                         } else {
504                                 return(LastIndexOf(value, this.length - 1,
505                                                    this.length));
506                         }
507                 }
508
509                 public int LastIndexOf(char value, int sindex){
510                         return(LastIndexOf(value, sindex, sindex + 1));
511                 }
512
513                 public int LastIndexOf(String value, int sindex) {
514                         return(LastIndexOf(value, sindex, sindex + 1));
515                 }
516
517                 /* This method is culture-insensitive */
518                 public int LastIndexOf(char value, int sindex, int count) {
519                         if (sindex == 0 && this.length == 0) {
520                                 return -1;
521                         }
522
523                         if (sindex < 0 || count < 0) {
524                                 throw new ArgumentOutOfRangeException ();
525                         }
526
527                         if (sindex >= this.length || sindex - count + 1 < 0) {
528                                 throw new ArgumentOutOfRangeException ();
529                         }
530                         
531                         for(int pos=sindex; pos > sindex - count; pos--) {
532                                 if(this[pos]==value) {
533                                         return(pos);
534                                 }
535                         }
536
537                         return(-1);
538                 }
539
540                 /* But this one is culture-sensitive */
541                 public int LastIndexOf(String value, int sindex, int count) {
542                         if (null == value) {
543                                 throw new ArgumentNullException();
544                         }
545
546                         if (value == String.Empty) {
547                                 return(0);
548                         }
549
550                         if (sindex == 0 && this.length == 0) {
551                                 return -1;
552                         }
553
554                         // This check is needed to match undocumented MS behaviour
555                         if (this.length == 0 && value.length > 0) {
556                                 return(-1);
557                         }
558
559                         if (value.length > sindex) {
560                                 return -1;
561                         }
562
563                         if (count == 0) {
564                                 return(-1);
565                         }
566
567                         if (sindex < 0 || sindex > this.length) {
568                                 throw new ArgumentOutOfRangeException ();
569                         }
570
571                         if (count < 0 || sindex - count + 1 < 0) {
572                                 throw new ArgumentOutOfRangeException ();
573                         }
574                         
575                         return(CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, sindex, count));
576                 }
577
578                 public String PadLeft(int width) {
579                         return PadLeft(width, ' ');
580                 }
581
582                 public String PadLeft(int width, char chr) {
583                         if (width < 0)
584                                 throw new ArgumentException();
585
586                         if (width < this.length)
587                                 return String.Copy(this);
588
589                         return InternalPad(width, chr, false);
590                 }
591
592                 public String PadRight(int width) {
593                         return PadRight(width, ' ');
594                 }
595
596                 public String PadRight(int width, char chr) {
597                         if (width < 0)
598                                 throw new ArgumentException();
599
600                         if (width < this.length)
601                                 return String.Copy(this);
602
603                         return InternalPad(width, chr, true);
604                 }
605
606                 public bool StartsWith(String value) {
607                         if (value == null) {
608                                 throw new ArgumentNullException("value");
609                         }
610
611                         if (this.length < value.length) {
612                                 return(false);
613                         }
614
615                         return (0 == Compare(this, 0, value, 0 , value.length));
616                 }
617         
618     
619                 /* This method is culture insensitive */
620                 public String Replace (char oldChar, char newChar) {
621                         return(InternalReplace(oldChar, newChar));
622                 }
623
624                 /* This method is culture sensitive */
625                 public String Replace(String oldValue, String newValue) {
626                         if(oldValue==null) {
627                                 throw new ArgumentNullException ("oldValue");
628                         }
629                         if(oldValue==String.Empty) {
630                                 throw new ArgumentException ("oldValue is the empty string.");
631                         }
632
633                         if(this==String.Empty) {
634                                 return(this);
635                         }
636
637                         if(newValue==null) {
638                                 newValue=String.Empty;
639                         }
640                         
641                         return(InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo));
642                 }
643
644                 public String Remove(int sindex, int count) {
645                         if (sindex < 0 || count < 0 || sindex + count > this.length)
646                                 throw new ArgumentOutOfRangeException ();
647
648                         return InternalRemove(sindex, count);
649                 }
650
651                 public String ToLower() {
652                         return(InternalToLower(CultureInfo.CurrentCulture));
653                 }
654
655                 public String ToLower(CultureInfo culture) {
656                         return(InternalToLower(culture));
657                 }
658
659                 public String ToUpper() {
660                         return(InternalToUpper(CultureInfo.CurrentCulture));
661                 }
662
663                 public String ToUpper(CultureInfo culture) {
664                         return(InternalToUpper(culture));
665                 }
666
667                 public override String ToString() {
668                         return this;
669                 }
670
671                 public String ToString(IFormatProvider provider) {
672                         return this;
673                 }
674
675                 public String Trim() {
676                         return Trim(null);
677                 }
678
679                 public static String Format(String format, Object arg0) {
680                         return Format(null, format, new Object[] {arg0});
681                 }
682
683                 public static String Format(String format, Object arg0, Object arg1) {
684                         return Format(null, format, new Object[] {arg0, arg1});
685                 }
686
687                 public static String Format(String format, Object arg0, Object arg1, Object arg2) {
688                         return Format(null, format, new Object[] {arg0, arg1, arg2});
689                 }
690
691                 public static string Format (string format, params object[] args) {
692                         return Format (null, format, args);
693                 }
694         
695                 public static string Format (IFormatProvider provider, string format, params object[] args) {
696                         StringBuilder b = new StringBuilder ();
697                         FormatHelper (b, provider, format, args);
698                         return b.ToString ();
699                 }
700                 
701                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args) {
702                         if (format == null || args == null)
703                                 throw new ArgumentNullException ();
704                         
705                         int ptr = 0;
706                         int start = ptr;
707                         while (ptr < format.length) {
708                                 char c = format[ptr ++];
709
710                                 if (c == '{') {
711                                         result.Append (format, start, ptr - start - 1);
712
713                                         // check for escaped open bracket
714
715                                         if (format[ptr] == '{') {
716                                                 start = ptr ++;
717                                                 continue;
718                                         }
719
720                                         // parse specifier
721                                 
722                                         int n, width;
723                                         bool left_align;
724                                         string arg_format;
725
726                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
727                                         if (n >= args.Length)
728                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
729
730                                         // format argument
731
732                                         object arg = args[n];
733
734                                         string str;
735                                         if (arg == null)
736                                                 str = "";
737                                         else if (arg is IFormattable)
738                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
739                                         else
740                                                 str = arg.ToString ();
741
742                                         // pad formatted string and append to result
743
744                                         if (width > str.length) {
745                                                 string pad = new String (' ', width - str.length);
746
747                                                 if (left_align) {
748                                                         result.Append (str);
749                                                         result.Append (pad);
750                                                 }
751                                                 else {
752                                                         result.Append (pad);
753                                                         result.Append (str);
754                                                 }
755                                         }
756                                         else
757                                                 result.Append (str);
758
759                                         start = ptr;
760                                 }
761                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
762                                         result.Append (format, start, ptr - start - 1);
763                                         start = ptr ++;
764                                 }
765                                 else if (c == '}') {
766                                         throw new FormatException ("Input string was not in a correct format.");
767                                 }
768                         }
769
770                         if (start < format.length)
771                                 result.Append (format.Substring (start));
772                 }
773
774                 public static String Copy (String str) {
775                         if (str == null)
776                                 throw new ArgumentNullException ();
777
778                         int length = str.length;
779
780                         String tmp = InternalAllocateStr(length);
781                         InternalStrcpy(tmp, 0, str);
782                         return tmp;
783                 }
784
785                 public static String Concat(Object obj) {
786                         if (null == obj)
787                                 return String.Empty;
788
789                         return obj.ToString();
790                 }
791
792                 public static String Concat(Object obj1, Object obj2)
793                 {
794                         string s1, s2;
795
796                         if (obj1 == null){
797                                 if (obj2 == null)
798                                         return String.Empty;
799                                 else
800                                         return obj2.ToString ();
801                         } else if (obj2 == null)
802                                 return obj1.ToString ();
803
804                         s1 = obj1.ToString ();
805                         s2 = obj2.ToString ();
806                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
807                         InternalStrcpy (tmp, 0, s1);
808                         InternalStrcpy (tmp, s1.length, s2);
809
810                         return tmp;
811                 }
812
813                 public static String Concat(Object obj1, Object obj2, Object obj3)
814                 {
815                         string s1, s2, s3;
816                         if (obj1 == null)
817                                 s1 = String.Empty;
818                         else
819                                 s1 = obj1.ToString ();
820     
821                         if (obj2 == null)
822                                 s2 = String.Empty;
823                         else
824                                 s2 = obj2.ToString ();
825     
826                         if (obj3 == null)
827                                 s3 = String.Empty;
828                         else
829                                 s3 = obj3.ToString ();
830     
831                         return Concat (s1, s2, s3);
832                 }
833
834                 public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
835                 {
836                         string s1, s2, s3, s4;
837
838                         if (obj1 == null)
839                                 s1 = String.Empty;
840                         else
841                                 s1 = obj1.ToString ();
842     
843                         if (obj2 == null)
844                                 s2 = String.Empty;
845                         else
846                                 s2 = obj2.ToString ();
847     
848                         if (obj3 == null)
849                                 s3 = String.Empty;
850                         else
851                                 s3 = obj3.ToString ();
852
853                         if (obj4 == null)
854                                 s4 = String.Empty;
855                         else
856                                 s4 = obj4.ToString ();
857                         
858                         return Concat (s1, s2, s3, s4);
859                         
860                 }
861
862                 public static String Concat(String s1, String s2)
863                 {
864                         if (s1 == null) {
865                                 if (s2 == null)
866                                         return String.Empty;
867                                 return s2;
868                         }
869
870                         if (s2 == null)
871                                 return s1; 
872
873                         String tmp = InternalAllocateStr(s1.length + s2.length);
874             
875                         InternalStrcpy(tmp, 0, s1);
876                         InternalStrcpy(tmp, s1.length, s2);
877             
878                         return tmp;
879                 }
880
881                 public static String Concat(String s1, String s2, String s3)
882                 {
883                         if (s1 == null){
884                                 if (s2 == null){
885                                         if (s3 == null)
886                                                 return String.Empty;
887                                         return s3;
888                                 } else {
889                                         if (s3 == null)
890                                                 return s2;
891                                 }
892                                 s1 = String.Empty;
893                         } else {
894                                 if (s2 == null){
895                                         if (s3 == null)
896                                                 return s1;
897                                         else
898                                                 s2 = String.Empty;
899                                 } else {
900                                         if (s3 == null)
901                                                 s3 = String.Empty;
902                                 }
903                         }
904                         
905                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length);
906
907                         InternalStrcpy(tmp, 0, s1);
908                         InternalStrcpy(tmp, s1.length, s2);
909                         InternalStrcpy(tmp, s1.length + s2.length, s3);
910
911                         return tmp;
912                 }
913
914                 public static String Concat(String s1, String s2, String s3, String s4) {
915                         if (null == s1 && null == s2 && null == s3 && null == s4) {
916                                 return String.Empty;
917                         }
918
919                         if (null == s1) { s1 = String.Empty; }
920                         if (null == s2) { s2 = String.Empty; }
921                         if (null == s3) { s3 = String.Empty; }
922                         if (null == s4) { s4 = String.Empty; }
923
924                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length + s4.length);
925
926                         InternalStrcpy(tmp, 0, s1);
927                         InternalStrcpy(tmp, s1.length, s2);
928                         InternalStrcpy(tmp, s1.length + s2.length, s3);
929                         InternalStrcpy(tmp, s1.length + s2.length + s3.length, s4);
930
931                         return tmp;
932                 }
933
934                 public static String Concat(params Object[] args) {
935                         string [] strings;
936                         int len, i, currentpos;
937
938                         if (null == args)
939                                 throw new ArgumentNullException ();
940
941                         strings = new string [args.Length];
942                         len = 0;
943                         i = 0;
944                         foreach (object arg in args) {
945                                 /* use Empty for each null argument */
946                                 if (arg == null)
947                                         strings[i] = String.Empty;
948                                 else
949                                         strings[i] = arg.ToString ();
950                                 len += strings[i].length;
951                                 i++;
952                         }
953
954                         if (len == 0)
955                                 return String.Empty;
956
957                         currentpos = 0;
958
959                         String tmp = InternalAllocateStr(len);
960                         for (i = 0; i < strings.Length; i++) {
961                                 InternalStrcpy(tmp, currentpos, strings[i]);
962                                 currentpos += strings[i].length;
963                         }
964
965                         return tmp;
966                 }
967
968                 public static String Concat(params String[] values) {
969                         int len, i, currentpos;
970
971                         if (values == null)
972                                 throw new ArgumentNullException ();
973
974                         len = 0;
975                         foreach (string value in values)
976                                 len += value != null ? value.length : 0;
977
978                         if (len == 0)
979                                 return String.Empty;
980
981                         currentpos = 0;
982
983                         String tmp = InternalAllocateStr(len);
984                         for (i = 0; i < values.Length; i++) {
985                                 if (values[i] == null)
986                                         continue;
987
988                                 InternalStrcpy(tmp, currentpos, values[i]);
989                                 currentpos += values[i].length;
990                         }       
991         
992                         return tmp;
993                 }
994
995                 public String Insert(int sindex, String value) {
996                         if (null == value)
997                                 throw new ArgumentNullException();
998
999                         if (sindex < 0 || sindex > this.length)
1000                                 throw new ArgumentOutOfRangeException();
1001         
1002                         return InternalInsert(sindex, value);
1003                 }
1004
1005
1006                 public static string Intern (string str) {
1007                         if (null == str)
1008                                 throw new ArgumentNullException ();
1009
1010                         return InternalIntern(str);
1011                 }
1012
1013                 public static string IsInterned (string str) {
1014                         if (null == str)
1015                                 throw new ArgumentNullException();
1016
1017                         return InternalIsInterned(str);
1018                 }
1019         
1020                 public static string Join (string separator, string [] value) {
1021                         if (value == null)
1022                                 throw new ArgumentNullException ();
1023
1024                         return Join(separator, value, 0, value.Length);
1025                 }
1026
1027                 public static string Join(string separator, string[] value, int sindex, int count) {
1028                         if (value == null)
1029                                 throw new ArgumentNullException ();
1030
1031                         if (sindex + count > value.Length)
1032                                 throw new ArgumentOutOfRangeException ();
1033
1034                         if (sindex == value.Length)
1035                                 return String.Empty;
1036
1037                         return InternalJoin(separator, value, sindex, count);
1038                 }
1039
1040                 bool IConvertible.ToBoolean (IFormatProvider provider) {
1041                         return Convert.ToBoolean (this);
1042                 }
1043                 
1044                 byte IConvertible.ToByte (IFormatProvider provider) {
1045                         return Convert.ToByte (this);
1046                 }
1047                 
1048                 char IConvertible.ToChar (IFormatProvider provider) {
1049                         return Convert.ToChar (this);
1050                 }
1051
1052                 DateTime IConvertible.ToDateTime (IFormatProvider provider) {
1053                         return Convert.ToDateTime (this);
1054                 }
1055
1056                 decimal IConvertible.ToDecimal (IFormatProvider provider) {
1057                         return Convert.ToDecimal (this);
1058                 }
1059
1060                 double IConvertible.ToDouble (IFormatProvider provider) {
1061                         return Convert.ToDouble (this);
1062                 }
1063
1064                 short IConvertible.ToInt16 (IFormatProvider provider) {
1065                         return Convert.ToInt16 (this);
1066                 }
1067
1068                 int IConvertible.ToInt32 (IFormatProvider provider) {
1069                         return Convert.ToInt32 (this);
1070                 }
1071
1072                 long IConvertible.ToInt64 (IFormatProvider provider) {
1073                         return Convert.ToInt64 (this);
1074                 }
1075         
1076                 [CLSCompliant(false)]
1077                 sbyte IConvertible.ToSByte (IFormatProvider provider) {
1078                         return Convert.ToSByte (this);
1079                 }
1080
1081                 float IConvertible.ToSingle (IFormatProvider provider) {
1082                         return Convert.ToSingle (this);
1083                 }
1084                 string IConvertible.ToString (IFormatProvider format) {
1085                         return this;
1086                 }
1087
1088                 object IConvertible.ToType (Type conversionType, IFormatProvider provider) {
1089                         return Convert.ToType (this, conversionType,  provider);
1090                 }
1091
1092                 [CLSCompliant(false)]
1093                 ushort IConvertible.ToUInt16 (IFormatProvider provider) {
1094                         return Convert.ToUInt16 (this);
1095                 }
1096
1097                 [CLSCompliant(false)]
1098                 uint IConvertible.ToUInt32 (IFormatProvider provider) {
1099                         return Convert.ToUInt32 (this);
1100                 }
1101
1102                 [CLSCompliant(false)]
1103                 ulong IConvertible.ToUInt64 (IFormatProvider provider) {
1104                         return Convert.ToUInt64 (this);
1105                 }
1106
1107                 TypeCode IConvertible.GetTypeCode () {
1108                         return TypeCode.String;
1109                 }
1110
1111                 public int Length {
1112                         get {
1113                                 return length;
1114                         }
1115                 }
1116
1117                 public CharEnumerator GetEnumerator () {
1118                         return new CharEnumerator (this);
1119                 }
1120                 
1121                 IEnumerator IEnumerable.GetEnumerator () {
1122                         return new CharEnumerator (this);
1123                 }
1124
1125                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1126                         // parses format specifier of form:
1127                         //   N,[\ +[-]M][:F]}
1128                         //
1129                         // where:
1130
1131                         try {
1132                                 // N = argument number (non-negative integer)
1133                         
1134                                 n = ParseDecimal (str, ref ptr);
1135                                 if (n < 0)
1136                                         throw new FormatException ("Input string was not in a correct format.");
1137                                 
1138                                 // M = width (non-negative integer)
1139
1140                                 if (str[ptr] == ',') {
1141                                         // White space between ',' and number or sign.
1142                                         int start = ++ptr;
1143                                         while (Char.IsWhiteSpace (str [ptr]))
1144                                                 ++ptr;
1145
1146                                         format = str.Substring (start, ptr - start);
1147
1148                                         left_align = (str [ptr] == '-');
1149                                         if (left_align)
1150                                                 ++ ptr;
1151
1152                                         width = ParseDecimal (str, ref ptr);
1153                                         if (width < 0)
1154                                                 throw new FormatException ("Input string was not in a correct format.");
1155                                 }
1156                                 else {
1157                                         width = 0;
1158                                         left_align = false;
1159                                         format = "";
1160                                 }
1161
1162                                 // F = argument format (string)
1163
1164                                 if (str[ptr] == ':') {
1165                                         int start = ++ ptr;
1166                                         while (str[ptr] != '}')
1167                                                 ++ ptr;
1168
1169                                         format += str.Substring (start, ptr - start);
1170                                 }
1171                                 else
1172                                         format = null;
1173
1174                                 if (str[ptr ++] != '}')
1175                                         throw new FormatException ("Input string was not in a correct format.");
1176                         }
1177                         catch (IndexOutOfRangeException) {
1178                                 throw new FormatException ("Input string was not in a correct format.");
1179                         }
1180                 }
1181
1182                 private static int ParseDecimal (string str, ref int ptr) {
1183                         int p = ptr;
1184                         int n = 0;
1185                         while (true) {
1186                                 char c = str[p];
1187                                 if (c < '0' || '9' < c)
1188                                         break;
1189
1190                                 n = n * 10 + c - '0';
1191                                 ++ p;
1192                         }
1193
1194                         if (p == ptr)
1195                                 return -1;
1196                         
1197                         ptr = p;
1198                         return n;
1199                 }
1200                 
1201                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1202                 unsafe public extern String(char *value);
1203
1204                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1205                 unsafe public extern String(char *value, int sindex, int length);
1206     
1207                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1208                 unsafe public extern String(sbyte *value);
1209
1210                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1211                 unsafe public extern String(sbyte *value, int sindex, int length);
1212
1213                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1214                 unsafe public extern String(sbyte *value, int sindex, int length, Encoding enc);
1215
1216                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1217                 public extern String(char [] val, int sindex, int length);
1218                 
1219                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1220                 public extern String(char [] val);
1221
1222                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1223                 public extern String(char c, int count);
1224         
1225                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1226                 public extern override int GetHashCode();
1227
1228                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1229                 private extern static string InternalJoin(string separator, string[] value, int sindex, int count);
1230                 
1231                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1232                 private extern String InternalInsert(int sindex, String value);
1233
1234                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1235                 private extern String InternalReplace(char oldChar, char newChar);
1236
1237                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1238                 private extern String InternalReplace(String oldValue, string newValue, CompareInfo comp);
1239                 
1240                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1241                 private extern String InternalRemove(int sindex, int count);
1242                 
1243                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1244                 private extern void InternalCopyTo(int sindex, char[] dest, int dindex, int count);
1245
1246                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1247                 private extern String[] InternalSplit(char[] separator, int count);
1248
1249                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1250                 private extern String InternalTrim(char[] chars, int typ);
1251
1252                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1253                 private extern int InternalIndexOfAny(char [] arr, int sindex, int count);
1254
1255                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1256                 private extern int InternalLastIndexOfAny(char [] anyOf, int sindex, int count);
1257
1258                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1259                 private extern String InternalPad(int width, char chr, bool right);
1260
1261                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1262                 private extern String InternalToLower(CultureInfo culture);
1263
1264                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1265                 private extern String InternalToUpper(CultureInfo culture);
1266
1267                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1268                 private extern static String InternalAllocateStr(int length);
1269
1270                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1271                 private extern static void InternalStrcpy(String dest, int destPos, String src);
1272
1273                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1274                 private extern static void InternalStrcpy(String dest, int destPos, String src, int startPos, int count);
1275
1276                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1277                 private extern static string InternalIntern(string str);
1278
1279                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1280                 private extern static string InternalIsInterned(string str);
1281         }
1282 }