2003-12-03 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 call
265                          * String.Substring on its arguments.
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 call
340                          * String.Substring on its arguments.
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                         return(CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, sindex, count));
422                 }
423                 
424                 /* But this one is culture-sensitive */
425                 public int IndexOf(String value, int sindex, int count) {
426                         if (value == null) {
427                                 throw new ArgumentNullException();
428                         }
429
430                         if (sindex < 0 || count < 0 ||
431                             sindex + count > this.length) {
432                                 throw new ArgumentOutOfRangeException ();
433                         }
434
435                         if (value.length == 0) {
436                                 return sindex;
437                         }
438                         
439                         if (sindex == 0 && this.length == 0) {
440                                 return -1;
441                         }
442
443                         if (count == 0) {
444                                 return(-1);
445                         }
446                         
447                         return(CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, sindex, count));
448                 }
449
450                 public int LastIndexOfAny(char [] arr) {
451                         if (null == arr) 
452                                 throw new ArgumentNullException();
453
454                         return InternalLastIndexOfAny(arr, this.length - 1, this.length);
455                 }
456
457                 public int LastIndexOfAny(char [] arr, int sindex) {
458                         if (null == arr) 
459                                 throw new ArgumentNullException();
460
461                         if (sindex < 0 || sindex > this.length)
462                                 throw new ArgumentOutOfRangeException();
463
464                         if (this.length == 0)
465                                 return -1;
466
467                         return InternalLastIndexOfAny(arr, sindex, sindex + 1);
468                 }
469
470                 public int LastIndexOfAny(char [] arr, int sindex, int count) {
471                         if (null == arr) 
472                                 throw new ArgumentNullException();
473
474                         if (sindex < 0 || count < 0 || sindex > this.length || sindex - count < -1)
475                                 throw new ArgumentOutOfRangeException();
476
477                         if (this.length == 0)
478                                 return -1;
479
480                         return InternalLastIndexOfAny(arr, sindex, count);
481                 }
482
483                 public int LastIndexOf(char value) {
484                         if(this.length==0) {
485                                 return(-1);
486                         } else {
487                                 return(LastIndexOf(value, this.length - 1,
488                                                    this.length));
489                         }
490                         
491                 }
492
493                 public int LastIndexOf(String value) {
494                         if(this.length==0) {
495                                 /* This overload does additional checking */
496                                 return(LastIndexOf(value, 0, 0));
497                         } else {
498                                 return(LastIndexOf(value, this.length - 1,
499                                                    this.length));
500                         }
501                 }
502
503                 public int LastIndexOf(char value, int sindex){
504                         return(LastIndexOf(value, sindex, sindex + 1));
505                 }
506
507                 public int LastIndexOf(String value, int sindex) {
508                         return(LastIndexOf(value, sindex, sindex + 1));
509                 }
510
511                 /* This method is culture-insensitive */
512                 public int LastIndexOf(char value, int sindex, int count) {
513                         if (sindex == 0 && this.length == 0) {
514                                 return -1;
515                         }
516
517                         if (sindex < 0 || count < 0) {
518                                 throw new ArgumentOutOfRangeException ();
519                         }
520
521                         if (sindex >= this.length || sindex - count + 1 < 0) {
522                                 throw new ArgumentOutOfRangeException ();
523                         }
524                         if (count == 0) {
525                                 return(-1);
526                         }
527                         
528                         return(CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, sindex, count));
529                 }
530
531                 /* But this one is culture-sensitive */
532                 public int LastIndexOf(String value, int sindex, int count) {
533                         if (null == value) {
534                                 throw new ArgumentNullException();
535                         }
536
537                         if (value == String.Empty) {
538                                 return(0);
539                         }
540
541                         if (sindex == 0 && this.length == 0) {
542                                 return -1;
543                         }
544
545                         // This check is needed to match undocumented MS behaviour
546                         if (this.length == 0 && value.length > 0) {
547                                 return(-1);
548                         }
549
550                         if (value.length > sindex) {
551                                 return -1;
552                         }
553
554                         if (count == 0) {
555                                 return(-1);
556                         }
557
558                         if (sindex < 0 || sindex > this.length) {
559                                 throw new ArgumentOutOfRangeException ();
560                         }
561
562                         if (count < 0 || sindex - count + 1 < 0) {
563                                 throw new ArgumentOutOfRangeException ();
564                         }
565                         
566                         return(CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, sindex, count));
567                 }
568
569                 public String PadLeft(int width) {
570                         return PadLeft(width, ' ');
571                 }
572
573                 public String PadLeft(int width, char chr) {
574                         if (width < 0)
575                                 throw new ArgumentException();
576
577                         if (width < this.length)
578                                 return String.Copy(this);
579
580                         return InternalPad(width, chr, false);
581                 }
582
583                 public String PadRight(int width) {
584                         return PadRight(width, ' ');
585                 }
586
587                 public String PadRight(int width, char chr) {
588                         if (width < 0)
589                                 throw new ArgumentException();
590
591                         if (width < this.length)
592                                 return String.Copy(this);
593
594                         return InternalPad(width, chr, true);
595                 }
596
597                 public bool StartsWith(String value) {
598                         if (value == null) {
599                                 throw new ArgumentNullException("value");
600                         }
601
602                         if (this.length < value.length) {
603                                 return(false);
604                         }
605
606                         return (0 == Compare(this, 0, value, 0 , value.length));
607                 }
608         
609     
610                 /* This method is culture insensitive */
611                 public String Replace (char oldChar, char newChar) {
612                         return(InternalReplace(oldChar, newChar));
613                 }
614
615                 /* This method is culture sensitive */
616                 public String Replace(String oldValue, String newValue) {
617                         if(oldValue==null) {
618                                 throw new ArgumentNullException ("oldValue");
619                         }
620                         if(oldValue==String.Empty) {
621                                 throw new ArgumentException ("oldValue is the empty string.");
622                         }
623
624                         if(this==String.Empty) {
625                                 return(this);
626                         }
627
628                         if(newValue==null) {
629                                 newValue=String.Empty;
630                         }
631                         
632                         return(InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo));
633                 }
634
635                 public String Remove(int sindex, int count) {
636                         if (sindex < 0 || count < 0 || sindex + count > this.length)
637                                 throw new ArgumentOutOfRangeException ();
638
639                         return InternalRemove(sindex, count);
640                 }
641
642                 public String ToLower() {
643                         return(InternalToLower(CultureInfo.CurrentCulture));
644                 }
645
646                 public String ToLower(CultureInfo culture) {
647                         return(InternalToLower(culture));
648                 }
649
650                 public String ToUpper() {
651                         return(InternalToUpper(CultureInfo.CurrentCulture));
652                 }
653
654                 public String ToUpper(CultureInfo culture) {
655                         return(InternalToUpper(culture));
656                 }
657
658                 public override String ToString() {
659                         return this;
660                 }
661
662                 public String ToString(IFormatProvider provider) {
663                         return this;
664                 }
665
666                 public String Trim() {
667                         return Trim(null);
668                 }
669
670                 public static String Format(String format, Object arg0) {
671                         return Format(null, format, new Object[] {arg0});
672                 }
673
674                 public static String Format(String format, Object arg0, Object arg1) {
675                         return Format(null, format, new Object[] {arg0, arg1});
676                 }
677
678                 public static String Format(String format, Object arg0, Object arg1, Object arg2) {
679                         return Format(null, format, new Object[] {arg0, arg1, arg2});
680                 }
681
682                 public static string Format (string format, params object[] args) {
683                         return Format (null, format, args);
684                 }
685         
686                 public static string Format (IFormatProvider provider, string format, params object[] args) {
687                         StringBuilder b = new StringBuilder ();
688                         FormatHelper (b, provider, format, args);
689                         return b.ToString ();
690                 }
691                 
692                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args) {
693                         if (format == null || args == null)
694                                 throw new ArgumentNullException ();
695                         
696                         int ptr = 0;
697                         int start = ptr;
698                         while (ptr < format.length) {
699                                 char c = format[ptr ++];
700
701                                 if (c == '{') {
702                                         result.Append (format, start, ptr - start - 1);
703
704                                         // check for escaped open bracket
705
706                                         if (format[ptr] == '{') {
707                                                 start = ptr ++;
708                                                 continue;
709                                         }
710
711                                         // parse specifier
712                                 
713                                         int n, width;
714                                         bool left_align;
715                                         string arg_format;
716
717                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
718                                         if (n >= args.Length)
719                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
720
721                                         // format argument
722
723                                         object arg = args[n];
724
725                                         string str;
726                                         if (arg == null)
727                                                 str = "";
728                                         else if (arg is IFormattable)
729                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
730                                         else
731                                                 str = arg.ToString ();
732
733                                         // pad formatted string and append to result
734
735                                         if (width > str.length) {
736                                                 string pad = new String (' ', width - str.length);
737
738                                                 if (left_align) {
739                                                         result.Append (str);
740                                                         result.Append (pad);
741                                                 }
742                                                 else {
743                                                         result.Append (pad);
744                                                         result.Append (str);
745                                                 }
746                                         }
747                                         else
748                                                 result.Append (str);
749
750                                         start = ptr;
751                                 }
752                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
753                                         result.Append (format, start, ptr - start - 1);
754                                         start = ptr ++;
755                                 }
756                                 else if (c == '}') {
757                                         throw new FormatException ("Input string was not in a correct format.");
758                                 }
759                         }
760
761                         if (start < format.length)
762                                 result.Append (format.Substring (start));
763                 }
764
765                 public static String Copy (String str) {
766                         if (str == null)
767                                 throw new ArgumentNullException ();
768
769                         int length = str.length;
770
771                         String tmp = InternalAllocateStr(length);
772                         InternalStrcpy(tmp, 0, str);
773                         return tmp;
774                 }
775
776                 public static String Concat(Object obj) {
777                         if (null == obj)
778                                 return String.Empty;
779
780                         return obj.ToString();
781                 }
782
783                 public static String Concat(Object obj1, Object obj2)
784                 {
785                         string s1, s2;
786
787                         if (obj1 == null){
788                                 if (obj2 == null)
789                                         return String.Empty;
790                                 else
791                                         return obj2.ToString ();
792                         } else if (obj2 == null)
793                                 return obj1.ToString ();
794
795                         s1 = obj1.ToString ();
796                         s2 = obj2.ToString ();
797                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
798                         InternalStrcpy (tmp, 0, s1);
799                         InternalStrcpy (tmp, s1.length, s2);
800
801                         return tmp;
802                 }
803
804                 public static String Concat(Object obj1, Object obj2, Object obj3)
805                 {
806                         string s1, s2, s3;
807                         if (obj1 == null)
808                                 s1 = String.Empty;
809                         else
810                                 s1 = obj1.ToString ();
811     
812                         if (obj2 == null)
813                                 s2 = String.Empty;
814                         else
815                                 s2 = obj2.ToString ();
816     
817                         if (obj3 == null)
818                                 s3 = String.Empty;
819                         else
820                                 s3 = obj3.ToString ();
821     
822                         return Concat (s1, s2, s3);
823                 }
824
825                 public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
826                 {
827                         string s1, s2, s3, s4;
828
829                         if (obj1 == null)
830                                 s1 = String.Empty;
831                         else
832                                 s1 = obj1.ToString ();
833     
834                         if (obj2 == null)
835                                 s2 = String.Empty;
836                         else
837                                 s2 = obj2.ToString ();
838     
839                         if (obj3 == null)
840                                 s3 = String.Empty;
841                         else
842                                 s3 = obj3.ToString ();
843
844                         if (obj4 == null)
845                                 s4 = String.Empty;
846                         else
847                                 s4 = obj4.ToString ();
848                         
849                         return Concat (s1, s2, s3, s4);
850                         
851                 }
852
853                 public static String Concat(String s1, String s2)
854                 {
855                         if (s1 == null) {
856                                 if (s2 == null)
857                                         return String.Empty;
858                                 return s2;
859                         }
860
861                         if (s2 == null)
862                                 return s1; 
863
864                         String tmp = InternalAllocateStr(s1.length + s2.length);
865             
866                         InternalStrcpy(tmp, 0, s1);
867                         InternalStrcpy(tmp, s1.length, s2);
868             
869                         return tmp;
870                 }
871
872                 public static String Concat(String s1, String s2, String s3)
873                 {
874                         if (s1 == null){
875                                 if (s2 == null){
876                                         if (s3 == null)
877                                                 return String.Empty;
878                                         return s3;
879                                 } else {
880                                         if (s3 == null)
881                                                 return s2;
882                                 }
883                                 s1 = String.Empty;
884                         } else {
885                                 if (s2 == null){
886                                         if (s3 == null)
887                                                 return s1;
888                                         else
889                                                 s2 = String.Empty;
890                                 } else {
891                                         if (s3 == null)
892                                                 s3 = String.Empty;
893                                 }
894                         }
895                         
896                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length);
897
898                         InternalStrcpy(tmp, 0, s1);
899                         InternalStrcpy(tmp, s1.length, s2);
900                         InternalStrcpy(tmp, s1.length + s2.length, s3);
901
902                         return tmp;
903                 }
904
905                 public static String Concat(String s1, String s2, String s3, String s4) {
906                         if (null == s1 && null == s2 && null == s3 && null == s4) {
907                                 return String.Empty;
908                         }
909
910                         if (null == s1) { s1 = String.Empty; }
911                         if (null == s2) { s2 = String.Empty; }
912                         if (null == s3) { s3 = String.Empty; }
913                         if (null == s4) { s4 = String.Empty; }
914
915                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length + s4.length);
916
917                         InternalStrcpy(tmp, 0, s1);
918                         InternalStrcpy(tmp, s1.length, s2);
919                         InternalStrcpy(tmp, s1.length + s2.length, s3);
920                         InternalStrcpy(tmp, s1.length + s2.length + s3.length, s4);
921
922                         return tmp;
923                 }
924
925                 public static String Concat(params Object[] args) {
926                         string [] strings;
927                         int len, i, currentpos;
928
929                         if (null == args)
930                                 throw new ArgumentNullException ();
931
932                         strings = new string [args.Length];
933                         len = 0;
934                         i = 0;
935                         foreach (object arg in args) {
936                                 /* use Empty for each null argument */
937                                 if (arg == null)
938                                         strings[i] = String.Empty;
939                                 else
940                                         strings[i] = arg.ToString ();
941                                 len += strings[i].length;
942                                 i++;
943                         }
944
945                         if (len == 0)
946                                 return String.Empty;
947
948                         currentpos = 0;
949
950                         String tmp = InternalAllocateStr(len);
951                         for (i = 0; i < strings.Length; i++) {
952                                 InternalStrcpy(tmp, currentpos, strings[i]);
953                                 currentpos += strings[i].length;
954                         }
955
956                         return tmp;
957                 }
958
959                 public static String Concat(params String[] values) {
960                         int len, i, currentpos;
961
962                         if (values == null)
963                                 throw new ArgumentNullException ();
964
965                         len = 0;
966                         foreach (string value in values)
967                                 len += value != null ? value.length : 0;
968
969                         if (len == 0)
970                                 return String.Empty;
971
972                         currentpos = 0;
973
974                         String tmp = InternalAllocateStr(len);
975                         for (i = 0; i < values.Length; i++) {
976                                 if (values[i] == null)
977                                         continue;
978
979                                 InternalStrcpy(tmp, currentpos, values[i]);
980                                 currentpos += values[i].length;
981                         }       
982         
983                         return tmp;
984                 }
985
986                 public String Insert(int sindex, String value) {
987                         if (null == value)
988                                 throw new ArgumentNullException();
989
990                         if (sindex < 0 || sindex > this.length)
991                                 throw new ArgumentOutOfRangeException();
992         
993                         return InternalInsert(sindex, value);
994                 }
995
996
997                 public static string Intern (string str) {
998                         if (null == str)
999                                 throw new ArgumentNullException ();
1000
1001                         return InternalIntern(str);
1002                 }
1003
1004                 public static string IsInterned (string str) {
1005                         if (null == str)
1006                                 throw new ArgumentNullException();
1007
1008                         return InternalIsInterned(str);
1009                 }
1010         
1011                 public static string Join (string separator, string [] value) {
1012                         if (value == null)
1013                                 throw new ArgumentNullException ();
1014
1015                         return Join(separator, value, 0, value.Length);
1016                 }
1017
1018                 public static string Join(string separator, string[] value, int sindex, int count) {
1019                         if (value == null)
1020                                 throw new ArgumentNullException ();
1021
1022                         if (sindex + count > value.Length)
1023                                 throw new ArgumentOutOfRangeException ();
1024
1025                         if (sindex == value.Length)
1026                                 return String.Empty;
1027
1028                         return InternalJoin(separator, value, sindex, count);
1029                 }
1030
1031                 bool IConvertible.ToBoolean (IFormatProvider provider) {
1032                         return Convert.ToBoolean (this);
1033                 }
1034                 
1035                 byte IConvertible.ToByte (IFormatProvider provider) {
1036                         return Convert.ToByte (this);
1037                 }
1038                 
1039                 char IConvertible.ToChar (IFormatProvider provider) {
1040                         return Convert.ToChar (this);
1041                 }
1042
1043                 DateTime IConvertible.ToDateTime (IFormatProvider provider) {
1044                         return Convert.ToDateTime (this);
1045                 }
1046
1047                 decimal IConvertible.ToDecimal (IFormatProvider provider) {
1048                         return Convert.ToDecimal (this);
1049                 }
1050
1051                 double IConvertible.ToDouble (IFormatProvider provider) {
1052                         return Convert.ToDouble (this);
1053                 }
1054
1055                 short IConvertible.ToInt16 (IFormatProvider provider) {
1056                         return Convert.ToInt16 (this);
1057                 }
1058
1059                 int IConvertible.ToInt32 (IFormatProvider provider) {
1060                         return Convert.ToInt32 (this);
1061                 }
1062
1063                 long IConvertible.ToInt64 (IFormatProvider provider) {
1064                         return Convert.ToInt64 (this);
1065                 }
1066         
1067                 [CLSCompliant(false)]
1068                 sbyte IConvertible.ToSByte (IFormatProvider provider) {
1069                         return Convert.ToSByte (this);
1070                 }
1071
1072                 float IConvertible.ToSingle (IFormatProvider provider) {
1073                         return Convert.ToSingle (this);
1074                 }
1075                 string IConvertible.ToString (IFormatProvider format) {
1076                         return this;
1077                 }
1078
1079                 object IConvertible.ToType (Type conversionType, IFormatProvider provider) {
1080                         return Convert.ToType (this, conversionType,  provider);
1081                 }
1082
1083                 [CLSCompliant(false)]
1084                 ushort IConvertible.ToUInt16 (IFormatProvider provider) {
1085                         return Convert.ToUInt16 (this);
1086                 }
1087
1088                 [CLSCompliant(false)]
1089                 uint IConvertible.ToUInt32 (IFormatProvider provider) {
1090                         return Convert.ToUInt32 (this);
1091                 }
1092
1093                 [CLSCompliant(false)]
1094                 ulong IConvertible.ToUInt64 (IFormatProvider provider) {
1095                         return Convert.ToUInt64 (this);
1096                 }
1097
1098                 TypeCode IConvertible.GetTypeCode () {
1099                         return TypeCode.String;
1100                 }
1101
1102                 public int Length {
1103                         get {
1104                                 return length;
1105                         }
1106                 }
1107
1108                 public CharEnumerator GetEnumerator () {
1109                         return new CharEnumerator (this);
1110                 }
1111                 
1112                 IEnumerator IEnumerable.GetEnumerator () {
1113                         return new CharEnumerator (this);
1114                 }
1115
1116                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1117                         // parses format specifier of form:
1118                         //   N,[\ +[-]M][:F]}
1119                         //
1120                         // where:
1121
1122                         try {
1123                                 // N = argument number (non-negative integer)
1124                         
1125                                 n = ParseDecimal (str, ref ptr);
1126                                 if (n < 0)
1127                                         throw new FormatException ("Input string was not in a correct format.");
1128                                 
1129                                 // M = width (non-negative integer)
1130
1131                                 if (str[ptr] == ',') {
1132                                         // White space between ',' and number or sign.
1133                                         int start = ++ptr;
1134                                         while (Char.IsWhiteSpace (str [ptr]))
1135                                                 ++ptr;
1136
1137                                         format = str.Substring (start, ptr - start);
1138
1139                                         left_align = (str [ptr] == '-');
1140                                         if (left_align)
1141                                                 ++ ptr;
1142
1143                                         width = ParseDecimal (str, ref ptr);
1144                                         if (width < 0)
1145                                                 throw new FormatException ("Input string was not in a correct format.");
1146                                 }
1147                                 else {
1148                                         width = 0;
1149                                         left_align = false;
1150                                         format = "";
1151                                 }
1152
1153                                 // F = argument format (string)
1154
1155                                 if (str[ptr] == ':') {
1156                                         int start = ++ ptr;
1157                                         while (str[ptr] != '}')
1158                                                 ++ ptr;
1159
1160                                         format += str.Substring (start, ptr - start);
1161                                 }
1162                                 else
1163                                         format = null;
1164
1165                                 if (str[ptr ++] != '}')
1166                                         throw new FormatException ("Input string was not in a correct format.");
1167                         }
1168                         catch (IndexOutOfRangeException) {
1169                                 throw new FormatException ("Input string was not in a correct format.");
1170                         }
1171                 }
1172
1173                 private static int ParseDecimal (string str, ref int ptr) {
1174                         int p = ptr;
1175                         int n = 0;
1176                         while (true) {
1177                                 char c = str[p];
1178                                 if (c < '0' || '9' < c)
1179                                         break;
1180
1181                                 n = n * 10 + c - '0';
1182                                 ++ p;
1183                         }
1184
1185                         if (p == ptr)
1186                                 return -1;
1187                         
1188                         ptr = p;
1189                         return n;
1190                 }
1191                 
1192                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1193                 unsafe public extern String(char *value);
1194
1195                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1196                 unsafe public extern String(char *value, int sindex, int length);
1197     
1198                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1199                 unsafe public extern String(sbyte *value);
1200
1201                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1202                 unsafe public extern String(sbyte *value, int sindex, int length);
1203
1204                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1205                 unsafe public extern String(sbyte *value, int sindex, int length, Encoding enc);
1206
1207                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1208                 public extern String(char [] val, int sindex, int length);
1209                 
1210                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1211                 public extern String(char [] val);
1212
1213                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1214                 public extern String(char c, int count);
1215         
1216                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1217                 public extern override int GetHashCode();
1218
1219                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1220                 private extern static string InternalJoin(string separator, string[] value, int sindex, int count);
1221                 
1222                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1223                 private extern String InternalInsert(int sindex, String value);
1224
1225                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1226                 private extern String InternalReplace(char oldChar, char newChar);
1227
1228                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1229                 private extern String InternalReplace(String oldValue, string newValue, CompareInfo comp);
1230                 
1231                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1232                 private extern String InternalRemove(int sindex, int count);
1233                 
1234                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1235                 private extern void InternalCopyTo(int sindex, char[] dest, int dindex, int count);
1236
1237                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1238                 private extern String[] InternalSplit(char[] separator, int count);
1239
1240                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1241                 private extern String InternalTrim(char[] chars, int typ);
1242
1243                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1244                 private extern int InternalIndexOfAny(char [] arr, int sindex, int count);
1245
1246                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1247                 private extern int InternalLastIndexOfAny(char [] anyOf, int sindex, int count);
1248
1249                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1250                 private extern String InternalPad(int width, char chr, bool right);
1251
1252                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1253                 private extern String InternalToLower(CultureInfo culture);
1254
1255                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1256                 private extern String InternalToUpper(CultureInfo culture);
1257
1258                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1259                 private extern static String InternalAllocateStr(int length);
1260
1261                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1262                 private extern static void InternalStrcpy(String dest, int destPos, String src);
1263
1264                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1265                 private extern static void InternalStrcpy(String dest, int destPos, String src, int startPos, int count);
1266
1267                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1268                 private extern static string InternalIntern(string str);
1269
1270                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1271                 private extern static string InternalIsInterned(string str);
1272         }
1273 }