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