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