2003-11-12 Miguel de Icaza <miguel@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                 }
185
186                 public static int Compare(String s1, String s2, bool inCase) {
187                         if (null == s1) {
188                                 if (null == s2)
189                                         return 0;
190                                 else
191                                         return -1;
192                         } else if (null == s2)
193                                 return 1;
194
195                         return InternalCompare(s1, 0, s2, 0, Math.Max(s1.length, s2.length), (inCase == true) ? COMPARE_INCASE : COMPARE_CASE);
196                 }
197                 
198                 [MonoTODO()]
199                 public static int Compare(String s1, String s2, bool inCase, CultureInfo culture) {
200                         return Compare(s1, s2, inCase);
201                 }
202
203                 public static int Compare(String s1, int i1, String s2, int i2, int length) {
204                         return Compare(s1, i1, s2, i2, length, false);
205                 }
206
207                 public static int Compare(String s1, int i1, String s2, int i2, int length, bool inCase) {
208                         if (null == s1) {
209                                 if (null == s2)
210                                         return 0;
211                                 else
212                                         return -1;
213                         } else if (null == s2)
214                                 return 1;
215
216                         if (length < 0 || i1 < 0 || i2 < 0)
217                                 throw new ArgumentOutOfRangeException ();
218
219                         if (i1 > s1.length || i2 > s2.length)
220                                 throw new ArgumentOutOfRangeException ();
221
222                         if (length == 0)
223                                 return 0;
224
225                         return InternalCompare(s1, i1, s2, i2, length, (inCase == true) ? COMPARE_INCASE : COMPARE_CASE);
226                 }
227
228                 [MonoTODO()]
229                 public static int Compare(String s1, int i1, String s2, int i2, int length, bool inCase, CultureInfo culture) {
230                         return Compare(s1, i1, s2, i2, length, inCase);
231                 }
232
233                 public int CompareTo(Object value) {
234                         if (null == value)
235                                 return 1;
236             
237                         if (!(value is String))
238                                 throw new ArgumentException();
239
240                         return String.Compare(this, (String) value, false);
241                 }
242
243                 public int CompareTo(String str) {
244                         if (null == str)
245                                 return 1;
246
247                         return Compare(this, str, false);
248                 }
249
250                 public static int CompareOrdinal(String s1, String s2) {
251                         if (null == s1 || null == s2) {
252                                 if ((Object)s1 == (Object) s2) {
253                                         return 0;
254                                 }
255
256                                 return (s1 == null) ? -1 : 1;
257                         }
258
259                         return InternalCompare(s1, 0, s2, 0, Math.Max(s1.length, s2.length), COMPARE_ORDINAL);
260                 }
261
262                 public static int CompareOrdinal(String s1, int i1, String s2, int i2, int length) {
263                         if (null == s1 || null == s2) {
264                                 if ((Object)s1 == (Object) s2) {
265                                         return 0;
266                                 }
267
268                                 return (s1 == null) ? -1 : 1;
269                         }
270
271                         if (i1 < 0 || i2 < 0 || length < 0)
272                                 throw new ArgumentOutOfRangeException ();
273
274                         if (i1 > s1.length || i2 > s2.length)
275                                 throw new ArgumentOutOfRangeException ();
276
277                         return InternalCompare(s1, i1, s2, i2, length, COMPARE_ORDINAL);
278                 }
279
280                 public bool EndsWith(String value) {
281                         if (null == value)
282                                 throw new ArgumentNullException();
283
284                         if (value.length > this.length) {
285                                 return false;
286                         }
287
288                         return (0 == Compare(this, length - value.length, value, 0, value.length));
289                 }
290         
291                 public int IndexOfAny(char [] arr) {
292                         if (null == arr)
293                                 throw new ArgumentNullException();
294
295                         return InternalIndexOfAny(arr, 0, this.length);
296                 }
297
298                 public int IndexOfAny(char [] arr, int sindex) {
299                         if (null == arr)
300                                 throw new ArgumentNullException();
301                         if (sindex < 0 || sindex >= this.length)
302                                 throw new ArgumentOutOfRangeException();
303
304                         return InternalIndexOfAny(arr, sindex, this.length - sindex);
305                 }
306
307                 public int IndexOfAny(char [] arr, int sindex, int count) {
308                         if (null == arr)
309                                 throw new ArgumentNullException();
310                         if (sindex < 0 || count < 0 || sindex + count > this.length)
311                                 throw new ArgumentOutOfRangeException ();
312
313                         return InternalIndexOfAny(arr, sindex, count);
314                 }
315
316                 public int IndexOf(char value) {
317                         return InternalIndexOf(value, 0, this.length);
318                 }
319
320                 public int IndexOf(String value) {
321                         return IndexOf(value, 0, this.length);
322                 }
323
324                 public int IndexOf(char value, int sindex) {
325                         if (sindex < 0 || sindex > this.length) {
326                                 throw new ArgumentOutOfRangeException();
327                         }
328
329                         if (sindex == this.length)
330                                 return -1;
331
332                         return InternalIndexOf(value, sindex, this.length - sindex);
333                 }
334
335                 public int IndexOf(String value, int sindex) {
336                         if (sindex == this.length)
337                                 return -1;
338
339                         return IndexOf(value, sindex, this.length - sindex);
340                 }
341
342                 public int IndexOf(char value, int sindex, int count) {
343                         if (sindex < 0 || count < 0 || sindex + count > this.length)
344                                 throw new ArgumentOutOfRangeException ();
345
346                         if (sindex == 0 && this.length == 0)
347                                 return -1;                      
348
349                         return InternalIndexOf(value, sindex, count);
350                 }
351                 
352                 public int IndexOf(String value, int sindex, int count) {
353                         if (null == value) 
354                                 throw new ArgumentNullException();
355
356                         if (sindex < 0 || count < 0 || sindex + count > this.length)
357                                 throw new ArgumentOutOfRangeException ();
358
359                         if (value.length == 0)
360                                 return sindex;
361                         
362                         if (sindex == 0 && this.length == 0)
363                                 return -1;
364
365                         return InternalIndexOf(value, sindex, count);
366                 }
367
368                 public int LastIndexOfAny(char [] arr) {
369                         if (null == arr) 
370                                 throw new ArgumentNullException();
371
372                         return InternalLastIndexOfAny(arr, this.length - 1, this.length);
373                 }
374
375                 public int LastIndexOfAny(char [] arr, int sindex) {
376                         if (null == arr) 
377                                 throw new ArgumentNullException();
378
379                         if (sindex < 0 || sindex > this.length)
380                                 throw new ArgumentOutOfRangeException();
381
382                         if (this.length == 0)
383                                 return -1;
384
385                         return InternalLastIndexOfAny(arr, sindex, sindex + 1);
386                 }
387
388                 public int LastIndexOfAny(char [] arr, int sindex, int count) {
389                         if (null == arr) 
390                                 throw new ArgumentNullException();
391
392                         if (sindex < 0 || count < 0 || sindex > this.length || sindex - count < -1)
393                                 throw new ArgumentOutOfRangeException();
394
395                         if (this.length == 0)
396                                 return -1;
397
398                         return InternalLastIndexOfAny(arr, sindex, count);
399                 }
400
401                 public int LastIndexOf(char value) {
402                         return InternalLastIndexOf(value, this.length - 1, this.length);
403                 }
404
405                 public int LastIndexOf(String value) {
406                         if (null == value) 
407                                 throw new ArgumentNullException();
408                         
409                         if (value.length == 0)
410                                 return length - 1;
411
412                         if (this.length == 0)
413                                 return -1;
414
415                         return InternalLastIndexOf(value, this.length - 1, this.length);
416                 }
417
418                 public int LastIndexOf(char value, int sindex){
419                         return LastIndexOf(value, sindex, sindex + 1);
420                 }
421
422                 public int LastIndexOf(String value, int sindex) {
423                         return LastIndexOf(value, sindex, sindex + 1);
424                 }
425
426                 public int LastIndexOf(char value, int sindex, int count) {
427                         if (sindex < 0 || count < 0)
428                                 throw new ArgumentOutOfRangeException ();
429
430                         if (sindex >= this.length || sindex - count + 1 < 0)
431                                 throw new ArgumentOutOfRangeException ();
432
433                         if (sindex == 0 && this.length == 0)
434                                 return -1;
435
436                         return InternalLastIndexOf(value, sindex, count);
437                 }
438
439                 public int LastIndexOf(String value, int sindex, int count) {
440                         if (null == value) 
441                                 throw new ArgumentNullException();
442
443                         // This check is needed to match undocumented MS behaviour
444                         if (this.length == 0 && value.length > 0)
445                                 return -1;
446
447                         if (sindex < 0 || sindex > this.length)
448                                 throw new ArgumentOutOfRangeException ();
449
450                         if (count < 0 || sindex - count + 1 < 0)
451                                 throw new ArgumentOutOfRangeException ();
452
453                         if (value.length > sindex)
454                                 return -1;
455
456                         if (value == String.Empty)
457                                 return sindex;
458
459                         if (sindex == 0 && this.length == 0)
460                                 return -1;
461
462                         return InternalLastIndexOf(value, sindex, count);
463                 }
464
465                 public String PadLeft(int width) {
466                         return PadLeft(width, ' ');
467                 }
468
469                 public String PadLeft(int width, char chr) {
470                         if (width < 0)
471                                 throw new ArgumentException();
472
473                         if (width < this.length)
474                                 return String.Copy(this);
475
476                         return InternalPad(width, chr, false);
477                 }
478
479                 public String PadRight(int width) {
480                         return PadRight(width, ' ');
481                 }
482
483                 public String PadRight(int width, char chr) {
484                         if (width < 0)
485                                 throw new ArgumentException();
486
487                         if (width < this.length)
488                                 return String.Copy(this);
489
490                         return InternalPad(width, chr, true);
491                 }
492
493                 public bool StartsWith(String value) {
494                         if (null == value)
495                                 throw new ArgumentNullException();
496
497                         if (this.length < value.length)
498                                 return false;
499
500                         return (0 == Compare(this, 0, value, 0 , value.length));
501                 }
502         
503     
504                 public String Replace (char oldChar, char newChar) {
505                         return InternalReplace(oldChar, newChar);
506                 }
507
508                 public String Replace(String oldValue, String newValue) {
509                         if (null == oldValue)
510                                 throw new ArgumentNullException();
511
512                         return InternalReplace(oldValue, newValue);
513                 }
514
515                 public String Remove(int sindex, int count) {
516                         if (sindex < 0 || count < 0 || sindex + count > this.length)
517                                 throw new ArgumentOutOfRangeException ();
518
519                         return InternalRemove(sindex, count);
520                 }
521
522                 public String ToLower() {
523                         return InternalToLower();
524                 }
525
526                 [MonoTODO("By now, don't use culture info")]
527                 public String ToLower(CultureInfo culture) {
528                         return InternalToLower();
529                 }
530
531                 public String ToUpper() {
532                         return InternalToUpper();
533                 }
534
535                 [MonoTODO("By now, don't use culture info")]
536                 public String ToUpper(CultureInfo culture) {
537                         return InternalToUpper();
538                 }
539
540                 public override String ToString() {
541                         return this;
542                 }
543
544                 public String ToString(IFormatProvider provider) {
545                         return this;
546                 }
547
548                 public String Trim() {
549                         return Trim(null);
550                 }
551
552                 public static String Format(String format, Object arg0) {
553                         return Format(null, format, new Object[] {arg0});
554                 }
555
556                 public static String Format(String format, Object arg0, Object arg1) {
557                         return Format(null, format, new Object[] {arg0, arg1});
558                 }
559
560                 public static String Format(String format, Object arg0, Object arg1, Object arg2) {
561                         return Format(null, format, new Object[] {arg0, arg1, arg2});
562                 }
563
564                 public static string Format (string format, params object[] args) {
565                         return Format (null, format, args);
566                 }
567         
568                 public static string Format (IFormatProvider provider, string format, params object[] args) {
569                         StringBuilder b = new StringBuilder ();
570                         FormatHelper (b, provider, format, args);
571                         return b.ToString ();
572                 }
573                 
574                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args) {
575                         if (format == null || args == null)
576                                 throw new ArgumentNullException ();
577                         
578                         int ptr = 0;
579                         int start = ptr;
580                         while (ptr < format.length) {
581                                 char c = format[ptr ++];
582
583                                 if (c == '{') {
584                                         result.Append (format, start, ptr - start - 1);
585
586                                         // check for escaped open bracket
587
588                                         if (format[ptr] == '{') {
589                                                 start = ptr ++;
590                                                 continue;
591                                         }
592
593                                         // parse specifier
594                                 
595                                         int n, width;
596                                         bool left_align;
597                                         string arg_format;
598
599                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
600                                         if (n >= args.Length)
601                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
602
603                                         // format argument
604
605                                         object arg = args[n];
606
607                                         string str;
608                                         if (arg == null)
609                                                 str = "";
610                                         else if (arg is IFormattable)
611                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
612                                         else
613                                                 str = arg.ToString ();
614
615                                         // pad formatted string and append to result
616
617                                         if (width > str.length) {
618                                                 string pad = new String (' ', width - str.length);
619
620                                                 if (left_align) {
621                                                         result.Append (str);
622                                                         result.Append (pad);
623                                                 }
624                                                 else {
625                                                         result.Append (pad);
626                                                         result.Append (str);
627                                                 }
628                                         }
629                                         else
630                                                 result.Append (str);
631
632                                         start = ptr;
633                                 }
634                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
635                                         result.Append (format, start, ptr - start - 1);
636                                         start = ptr ++;
637                                 }
638                                 else if (c == '}') {
639                                         throw new FormatException ("Input string was not in a correct format.");
640                                 }
641                         }
642
643                         if (start < format.length)
644                                 result.Append (format.Substring (start));
645                 }
646
647                 public static String Copy (String str) {
648                         if (str == null)
649                                 throw new ArgumentNullException ();
650
651                         int length = str.length;
652
653                         String tmp = InternalAllocateStr(length);
654                         InternalStrcpy(tmp, 0, str);
655                         return tmp;
656                 }
657
658                 public static String Concat(Object obj) {
659                         if (null == obj)
660                                 return String.Empty;
661
662                         return obj.ToString();
663                 }
664
665                 public static String Concat(Object obj1, Object obj2)
666                 {
667                         string s1, s2;
668
669                         if (obj1 == null){
670                                 if (obj2 == null)
671                                         return String.Empty;
672                                 else
673                                         return obj2.ToString ();
674                         } else if (obj2 == null)
675                                 return obj1.ToString ();
676
677                         s1 = obj1.ToString ();
678                         s2 = obj2.ToString ();
679                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
680                         InternalStrcpy (tmp, 0, s1);
681                         InternalStrcpy (tmp, s1.length, s2);
682
683                         return tmp;
684                 }
685
686                 public static String Concat(Object obj1, Object obj2, Object obj3)
687                 {
688                         string s1, s2, s3;
689                         if (obj1 == null)
690                                 s1 = String.Empty;
691                         else
692                                 s1 = obj1.ToString ();
693     
694                         if (obj2 == null)
695                                 s2 = String.Empty;
696                         else
697                                 s2 = obj2.ToString ();
698     
699                         if (obj3 == null)
700                                 s3 = String.Empty;
701                         else
702                                 s3 = obj3.ToString ();
703     
704                         return Concat (s1, s2, s3);
705                 }
706
707                 public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
708                 {
709                         string s1, s2, s3, s4;
710
711                         if (obj1 == null)
712                                 s1 = String.Empty;
713                         else
714                                 s1 = obj1.ToString ();
715     
716                         if (obj2 == null)
717                                 s2 = String.Empty;
718                         else
719                                 s2 = obj2.ToString ();
720     
721                         if (obj3 == null)
722                                 s3 = String.Empty;
723                         else
724                                 s3 = obj3.ToString ();
725
726                         if (obj4 == null)
727                                 s4 = String.Empty;
728                         else
729                                 s4 = obj4.ToString ();
730                         
731                         return Concat (s1, s2, s3, s4);
732                         
733                 }
734
735                 public static String Concat(String s1, String s2)
736                 {
737                         if (s1 == null) {
738                                 if (s2 == null)
739                                         return String.Empty;
740                                 return s2;
741                         }
742
743                         if (s2 == null)
744                                 return s1; 
745
746                         String tmp = InternalAllocateStr(s1.length + s2.length);
747             
748                         InternalStrcpy(tmp, 0, s1);
749                         InternalStrcpy(tmp, s1.length, s2);
750             
751                         return tmp;
752                 }
753
754                 public static String Concat(String s1, String s2, String s3)
755                 {
756                         if (s1 == null){
757                                 if (s2 == null){
758                                         if (s3 == null)
759                                                 return String.Empty;
760                                         return s3;
761                                 } else {
762                                         if (s3 == null)
763                                                 return s2;
764                                 }
765                                 s1 = String.Empty;
766                         } else {
767                                 if (s2 == null){
768                                         if (s3 == null)
769                                                 return s1;
770                                         else
771                                                 s2 = String.Empty;
772                                 } else {
773                                         if (s3 == null)
774                                                 s3 = String.Empty;
775                                 }
776                         }
777                         
778                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length);
779
780                         InternalStrcpy(tmp, 0, s1);
781                         InternalStrcpy(tmp, s1.length, s2);
782                         InternalStrcpy(tmp, s1.length + s2.length, s3);
783
784                         return tmp;
785                 }
786
787                 public static String Concat(String s1, String s2, String s3, String s4) {
788                         if (null == s1 && null == s2 && null == s3 && null == s4) {
789                                 return String.Empty;
790                         }
791
792                         if (null == s1) { s1 = String.Empty; }
793                         if (null == s2) { s2 = String.Empty; }
794                         if (null == s3) { s3 = String.Empty; }
795                         if (null == s4) { s4 = String.Empty; }
796
797                         String tmp = InternalAllocateStr(s1.length + s2.length + s3.length + s4.length);
798
799                         InternalStrcpy(tmp, 0, s1);
800                         InternalStrcpy(tmp, s1.length, s2);
801                         InternalStrcpy(tmp, s1.length + s2.length, s3);
802                         InternalStrcpy(tmp, s1.length + s2.length + s3.length, s4);
803
804                         return tmp;
805                 }
806
807                 public static String Concat(params Object[] args) {
808                         string [] strings;
809                         int len, i, currentpos;
810
811                         if (null == args)
812                                 throw new ArgumentNullException ();
813
814                         strings = new string [args.Length];
815                         len = 0;
816                         i = 0;
817                         foreach (object arg in args) {
818                                 /* use Empty for each null argument */
819                                 if (arg == null)
820                                         strings[i] = String.Empty;
821                                 else
822                                         strings[i] = arg.ToString ();
823                                 len += strings[i].length;
824                                 i++;
825                         }
826
827                         if (len == 0)
828                                 return String.Empty;
829
830                         currentpos = 0;
831
832                         String tmp = InternalAllocateStr(len);
833                         for (i = 0; i < strings.Length; i++) {
834                                 InternalStrcpy(tmp, currentpos, strings[i]);
835                                 currentpos += strings[i].length;
836                         }
837
838                         return tmp;
839                 }
840
841                 public static String Concat(params String[] values) {
842                         int len, i, currentpos;
843
844                         if (values == null)
845                                 throw new ArgumentNullException ();
846
847                         len = 0;
848                         foreach (string value in values)
849                                 len += value != null ? value.length : 0;
850
851                         if (len == 0)
852                                 return String.Empty;
853
854                         currentpos = 0;
855
856                         String tmp = InternalAllocateStr(len);
857                         for (i = 0; i < values.Length; i++) {
858                                 if (values[i] == null)
859                                         continue;
860
861                                 InternalStrcpy(tmp, currentpos, values[i]);
862                                 currentpos += values[i].length;
863                         }       
864         
865                         return tmp;
866                 }
867
868                 public String Insert(int sindex, String value) {
869                         if (null == value)
870                                 throw new ArgumentNullException();
871
872                         if (sindex < 0 || sindex > this.length)
873                                 throw new ArgumentOutOfRangeException();
874         
875                         return InternalInsert(sindex, value);
876                 }
877
878
879                 public static string Intern (string str) {
880                         if (null == str)
881                                 throw new ArgumentNullException ();
882
883                         return InternalIntern(str);
884                 }
885
886                 public static string IsInterned (string str) {
887                         if (null == str)
888                                 throw new ArgumentNullException();
889
890                         return InternalIsInterned(str);
891                 }
892         
893                 public static string Join (string separator, string [] value) {
894                         if (value == null)
895                                 throw new ArgumentNullException ();
896
897                         return Join(separator, value, 0, value.Length);
898                 }
899
900                 public static string Join(string separator, string[] value, int sindex, int count) {
901                         if (value == null)
902                                 throw new ArgumentNullException ();
903
904                         if (sindex + count > value.Length)
905                                 throw new ArgumentOutOfRangeException ();
906
907                         if (sindex == value.Length)
908                                 return String.Empty;
909
910                         return InternalJoin(separator, value, sindex, count);
911                 }
912
913                 bool IConvertible.ToBoolean (IFormatProvider provider) {
914                         return Convert.ToBoolean (this);
915                 }
916                 
917                 byte IConvertible.ToByte (IFormatProvider provider) {
918                         return Convert.ToByte (this);
919                 }
920                 
921                 char IConvertible.ToChar (IFormatProvider provider) {
922                         return Convert.ToChar (this);
923                 }
924
925                 DateTime IConvertible.ToDateTime (IFormatProvider provider) {
926                         return Convert.ToDateTime (this);
927                 }
928
929                 decimal IConvertible.ToDecimal (IFormatProvider provider) {
930                         return Convert.ToDecimal (this);
931                 }
932
933                 double IConvertible.ToDouble (IFormatProvider provider) {
934                         return Convert.ToDouble (this);
935                 }
936
937                 short IConvertible.ToInt16 (IFormatProvider provider) {
938                         return Convert.ToInt16 (this);
939                 }
940
941                 int IConvertible.ToInt32 (IFormatProvider provider) {
942                         return Convert.ToInt32 (this);
943                 }
944
945                 long IConvertible.ToInt64 (IFormatProvider provider) {
946                         return Convert.ToInt64 (this);
947                 }
948         
949                 [CLSCompliant(false)]
950                 sbyte IConvertible.ToSByte (IFormatProvider provider) {
951                         return Convert.ToSByte (this);
952                 }
953
954                 float IConvertible.ToSingle (IFormatProvider provider) {
955                         return Convert.ToSingle (this);
956                 }
957                 string IConvertible.ToString (IFormatProvider format) {
958                         return this;
959                 }
960
961                 object IConvertible.ToType (Type conversionType, IFormatProvider provider) {
962                         return Convert.ToType (this, conversionType,  provider);
963                 }
964
965                 [CLSCompliant(false)]
966                 ushort IConvertible.ToUInt16 (IFormatProvider provider) {
967                         return Convert.ToUInt16 (this);
968                 }
969
970                 [CLSCompliant(false)]
971                 uint IConvertible.ToUInt32 (IFormatProvider provider) {
972                         return Convert.ToUInt32 (this);
973                 }
974
975                 [CLSCompliant(false)]
976                 ulong IConvertible.ToUInt64 (IFormatProvider provider) {
977                         return Convert.ToUInt64 (this);
978                 }
979
980                 TypeCode IConvertible.GetTypeCode () {
981                         return TypeCode.String;
982                 }
983
984                 public int Length {
985                         get {
986                                 return length;
987                         }
988                 }
989
990                 public CharEnumerator GetEnumerator () {
991                         return new CharEnumerator (this);
992                 }
993                 
994                 IEnumerator IEnumerable.GetEnumerator () {
995                         return new CharEnumerator (this);
996                 }
997
998                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
999                         // parses format specifier of form:
1000                         //   N,[\ +[-]M][:F]}
1001                         //
1002                         // where:
1003
1004                         try {
1005                                 // N = argument number (non-negative integer)
1006                         
1007                                 n = ParseDecimal (str, ref ptr);
1008                                 if (n < 0)
1009                                         throw new FormatException ("Input string was not in a correct format.");
1010                                 
1011                                 // M = width (non-negative integer)
1012
1013                                 if (str[ptr] == ',') {
1014                                         // White space between ',' and number or sign.
1015                                         int start = ++ptr;
1016                                         while (Char.IsWhiteSpace (str [ptr]))
1017                                                 ++ptr;
1018
1019                                         format = str.Substring (start, ptr - start);
1020
1021                                         left_align = (str [ptr] == '-');
1022                                         if (left_align)
1023                                                 ++ ptr;
1024
1025                                         width = ParseDecimal (str, ref ptr);
1026                                         if (width < 0)
1027                                                 throw new FormatException ("Input string was not in a correct format.");
1028                                 }
1029                                 else {
1030                                         width = 0;
1031                                         left_align = false;
1032                                         format = "";
1033                                 }
1034
1035                                 // F = argument format (string)
1036
1037                                 if (str[ptr] == ':') {
1038                                         int start = ++ ptr;
1039                                         while (str[ptr] != '}')
1040                                                 ++ ptr;
1041
1042                                         format += str.Substring (start, ptr - start);
1043                                 }
1044                                 else
1045                                         format = null;
1046
1047                                 if (str[ptr ++] != '}')
1048                                         throw new FormatException ("Input string was not in a correct format.");
1049                         }
1050                         catch (IndexOutOfRangeException) {
1051                                 throw new FormatException ("Input string was not in a correct format.");
1052                         }
1053                 }
1054
1055                 private static int ParseDecimal (string str, ref int ptr) {
1056                         int p = ptr;
1057                         int n = 0;
1058                         while (true) {
1059                                 char c = str[p];
1060                                 if (c < '0' || '9' < c)
1061                                         break;
1062
1063                                 n = n * 10 + c - '0';
1064                                 ++ p;
1065                         }
1066
1067                         if (p == ptr)
1068                                 return -1;
1069                         
1070                         ptr = p;
1071                         return n;
1072                 }
1073                 
1074                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1075                 unsafe public extern String(char *value);
1076
1077                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1078                 unsafe public extern String(char *value, int sindex, int length);
1079     
1080                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1081                 unsafe public extern String(sbyte *value);
1082
1083                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1084                 unsafe public extern String(sbyte *value, int sindex, int length);
1085
1086                 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
1087                 unsafe public extern String(sbyte *value, int sindex, int length, Encoding enc);
1088
1089                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1090                 public extern String(char [] val, int sindex, int length);
1091                 
1092                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1093                 public extern String(char [] val);
1094
1095                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1096                 public extern String(char c, int count);
1097         
1098                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1099                 public extern override int GetHashCode();
1100
1101                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1102                 private extern static string InternalJoin(string separator, string[] value, int sindex, int count);
1103                 
1104                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1105                 private extern String InternalInsert(int sindex, String value);
1106
1107                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1108                 private extern String InternalReplace(char oldChar, char newChar);
1109
1110                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1111                 private extern String InternalReplace(String oldValue, String newValue);
1112     
1113                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1114                 private extern String InternalRemove(int sindex, int count);
1115                 
1116                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1117                 private extern void InternalCopyTo(int sindex, char[] dest, int dindex, int count);
1118
1119                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1120                 private extern String[] InternalSplit(char[] separator, int count);
1121
1122                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1123                 private extern String InternalTrim(char[] chars, int typ);
1124
1125                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1126                 private extern int InternalIndexOf(char value, int sindex, int count);
1127
1128                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1129                 private extern int InternalIndexOf(string value, int sindex, int count);
1130
1131                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1132                 private extern int InternalIndexOfAny(char [] arr, int sindex, int count);
1133
1134                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1135                 private extern int InternalLastIndexOf(char value, int sindex, int count);
1136
1137                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1138                 private extern int InternalLastIndexOf(String value, int sindex, int count);
1139
1140                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1141                 private extern int InternalLastIndexOfAny(char [] anyOf, int sindex, int count);
1142
1143                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1144                 private extern String InternalPad(int width, char chr, bool right);
1145
1146                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1147                 private extern String InternalToLower();
1148
1149                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1150                 private extern String InternalToUpper();
1151
1152                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1153                 private extern static String InternalAllocateStr(int length);
1154
1155                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1156                 private extern static void InternalStrcpy(String dest, int destPos, String src);
1157
1158                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1159                 private extern static void InternalStrcpy(String dest, int destPos, String src, int startPos, int count);
1160
1161                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1162                 private extern static string InternalIntern(string str);
1163
1164                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1165                 private extern static string InternalIsInterned(string str);
1166
1167                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1168                 private extern static int InternalCompare(String s1, int i1, String s2, int i2, int length, int mode);
1169
1170                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1171                 private extern static bool InternalEquals(String s1, String s2);
1172         }
1173 }