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