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