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