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