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