This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / corlib / System / String.cs
1 //
2 // System.String.cs
3 //
4 // Authors:
5 //   Patrik Torstensson
6 //   Jeffrey Stedfast (fejj@ximian.com)
7 //   Dan Lewis (dihlewis@yahoo.co.uk)
8 //   Sebastien Pouliot  <sebastien@ximian.com>
9 //
10 // (C) 2001 Ximian, Inc.  http://www.ximian.com
11 // Copyright (C) 2004 Novell (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Text;
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
38
39 namespace System
40 {
41         [Serializable]
42         public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable
43         {
44                 [NonSerialized] private int length;
45                 [NonSerialized] private char start_char;
46
47                 private const int COMPARE_CASE = 0;
48                 private const int COMPARE_INCASE = 1;
49                 private const int COMPARE_ORDINAL = 2;
50
51                 public static readonly String Empty = "";
52
53                 public static unsafe bool Equals (string a, string b)
54                 {
55                         if ((a as object) == (b as object))
56                                 return true;
57
58                         if (a == null || b == null)
59                                 return false;
60
61                         int len = a.length;
62
63                         if (len != b.length)
64                                 return false;
65
66                         if (len == 0)
67                                 return true;
68
69                         fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
70                                 // it must be one char, because 0 len is done above
71                                 if (len < 2)
72                                         return *s1 == *s2;
73
74                                 // check by twos
75                                 int * sint1 = (int *) s1, sint2 = (int *) s2;
76                                 int n2 = len >> 1;
77                                 do {
78                                         if (*sint1++ != *sint2++)
79                                                 return false;
80                                 } while (--n2 != 0);
81
82                                 // nothing left
83                                 if ((len & 1) == 0)
84                                         return true;
85
86                                 // check the last one
87                                 return *(char *) sint1 == *(char *) sint2;
88                         }
89                 }
90
91                 public static bool operator == (String a, String b)
92                 {
93                         return Equals (a, b);
94                 }
95
96                 public static bool operator != (String a, String b)
97                 {
98                         return !Equals (a, b);
99                 }
100
101                 public override bool Equals (Object obj)
102                 {
103                         return Equals (this, obj as String);
104                 }
105
106                 public bool Equals (String value)
107                 {
108                         return Equals (this, value);
109                 }
110
111                 [IndexerName ("Chars")]
112                 public extern char this [int index] {
113                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
114                         get;
115                 }
116
117                 public Object Clone ()
118                 {
119                         return this;
120                 }
121
122                 public TypeCode GetTypeCode ()
123                 {
124                         return TypeCode.String;
125                 }
126
127                 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
128                 {
129                         // LAMESPEC: should I null-terminate?
130                         if (destination == null)
131                                 throw new ArgumentNullException ("destination");
132
133                         if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
134                                 throw new ArgumentOutOfRangeException (); 
135
136                         // re-ordered to avoid possible integer overflow
137                         if (sourceIndex > Length - count)
138                                 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
139                         // re-ordered to avoid possible integer overflow
140                         if (destinationIndex > destination.Length - count)
141                                 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
142
143                         InternalCopyTo (sourceIndex, destination, destinationIndex, count);
144                 }
145
146                 public char[] ToCharArray ()
147                 {
148                         return ToCharArray (0, length);
149                 }
150
151                 public char[] ToCharArray (int startIndex, int length)
152                 {
153                         if (startIndex < 0)
154                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0"); 
155                         if (length < 0)
156                                 throw new ArgumentOutOfRangeException ("length", "< 0"); 
157                         // re-ordered to avoid possible integer overflow
158                         if (startIndex > this.length - length)
159                                 throw new ArgumentOutOfRangeException ("startIndex + length > this.length"); 
160
161                         char[] tmp = new char [length];
162
163                         InternalCopyTo (startIndex, tmp, 0, length);
164
165                         return tmp;
166                 }
167
168                 public String [] Split (params char [] separator)
169                 {
170                         return Split (separator, Int32.MaxValue);
171                 }
172
173                 public String[] Split (char[] separator, int count)
174                 {
175                         if (separator == null || separator.Length == 0)
176                                 separator = WhiteChars;
177
178                         if (count < 0)
179                                 throw new ArgumentOutOfRangeException ("count");
180
181                         if (count == 0) 
182                                 return new String[0];
183
184                         if (count == 1) 
185                                 return new String[1] { ToString() };
186
187                         return InternalSplit (separator, count);
188                 }
189
190                 public String Substring (int startIndex)
191                 {
192                         if (startIndex < 0 || startIndex > this.length)
193                                 throw new ArgumentOutOfRangeException ("startIndex");
194
195                         string tmp = InternalAllocateStr (this.length - startIndex);
196                         InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
197
198                         return tmp;
199                 }
200
201                 public String Substring (int startIndex, int length)
202                 {
203                         if (length < 0)
204                                 throw new ArgumentOutOfRangeException ("length", "< 0");
205                         if (startIndex < 0)
206                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
207                         // re-ordered to avoid possible integer overflow
208                         if (startIndex > this.length - length)
209                                 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
210
211                         if (length == 0)
212                                 return String.Empty;
213
214                         string tmp = InternalAllocateStr (length);
215                         InternalStrcpy (tmp, 0, this, startIndex, length);
216
217                         return tmp;
218                 }       
219
220                 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
221                         (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
222                         (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
223                         (char) 0x3000, (char) 0xFEFF };
224
225                 public String Trim (params char[] trimChars)
226                 {
227                         if (trimChars == null || trimChars.Length == 0)
228                                 trimChars = WhiteChars;
229
230                         return InternalTrim (trimChars, 0);
231                 }
232
233                 public String TrimStart (params char[] trimChars)
234                 {
235                         if (trimChars == null || trimChars.Length == 0)
236                                 trimChars = WhiteChars;
237
238                         return InternalTrim (trimChars, 1);
239                 }
240
241                 public String TrimEnd (params char[] trimChars)
242                 {
243                         if (trimChars == null || trimChars.Length == 0)
244                                 trimChars = WhiteChars;
245
246                         return InternalTrim (trimChars, 2);
247                 }
248
249                 public static int Compare (String strA, String strB)
250                 {
251                         return Compare (strA, strB, false, CultureInfo.CurrentCulture);
252                 }
253
254                 public static int Compare (String strA, String strB, bool ignoreCase)
255                 {
256                         return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
257                 }
258
259                 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
260                 {
261                         if (culture == null)
262                                 throw new ArgumentNullException ("culture");
263
264                         if (strA == null) {
265                                 if (strB == null)
266                                         return 0;
267                                 else
268                                         return -1;
269
270                         }
271                         else if (strB == null) {
272                                 return 1;
273                         }
274
275                         CompareOptions compopts;
276
277                         if (ignoreCase)
278                                 compopts = CompareOptions.IgnoreCase;
279                         else
280                                 compopts = CompareOptions.None;
281
282                         return culture.CompareInfo.Compare (strA, strB, compopts);
283                 }
284
285                 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
286                 {
287                         return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
288                 }
289
290                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
291                 {
292                         return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
293                 }
294                 
295                 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
296                 {
297                         if (culture == null)
298                                 throw new ArgumentNullException ("culture");
299
300                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
301                                 throw new ArgumentOutOfRangeException ();
302
303                         if (length == 0)
304                                 return 0;
305                         
306                         if (strA == null) {
307                                 if (strB == null) {
308                                         return 0;
309                                 } else {
310                                         return -1;
311                                 }
312                         }
313                         else if (strB == null) {
314                                 return 1;
315                         }
316
317                         CompareOptions compopts;
318
319                         if (ignoreCase)
320                                 compopts = CompareOptions.IgnoreCase;
321                         else
322                                 compopts = CompareOptions.None;
323
324                         /* Need to cap the requested length to the
325                          * length of the string, because
326                          * CompareInfo.Compare will insist that length
327                          * <= (string.Length - offset)
328                          */
329                         int len1 = length;
330                         int len2 = length;
331                         
332                         if (length > (strA.Length - indexA)) {
333                                 len1 = strA.Length - indexA;
334                         }
335
336                         if (length > (strB.Length - indexB)) {
337                                 len2 = strB.Length - indexB;
338                         }
339
340                         return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
341                 }
342
343                 public int CompareTo (Object value)
344                 {
345                         if (value == null)
346                                 return 1;
347
348                         if (!(value is String))
349                                 throw new ArgumentException ();
350
351                         return String.Compare (this, (String) value, false);
352                 }
353
354                 public int CompareTo (String strB)
355                 {
356                         if (strB == null)
357                                 return 1;
358
359                         return Compare (this, strB, false);
360                 }
361
362                 public static int CompareOrdinal (String strA, String strB)
363                 {
364                         if (strA == null) {
365                                 if (strB == null)
366                                         return 0;
367                                 else
368                                         return -1;
369                         }
370                         else if (strB == null) {
371                                 return 1;
372                         }
373
374                         /* Invariant, because that is cheaper to
375                          * instantiate (and chances are it already has
376                          * been.)
377                          */
378                         return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
379                 }
380
381                 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
382                 {
383                         if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
384                                 throw new ArgumentOutOfRangeException ();
385
386                         if (strA == null) {
387                                 if (strB == null)
388                                         return 0;
389                                 else
390                                         return -1;
391                         }
392                         else if (strB == null) {
393                                 return 1;
394                         }
395
396                         /* Need to cap the requested length to the
397                          * length of the string, because
398                          * CompareInfo.Compare will insist that length
399                          * <= (string.Length - offset)
400                          */
401                         int len1 = length;
402                         int len2 = length;
403
404                         if (length > (strA.Length - indexA)) {
405                                 len1 = strA.Length - indexA;
406                         }
407
408                         if (length > (strB.Length - indexB)) {
409                                 len2 = strB.Length - indexB;
410                         }
411
412                         return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
413                 }
414
415                 public bool EndsWith (String value)
416                 {
417                         if (value == null)
418                                 throw new ArgumentNullException ("value");
419
420                         if (value == String.Empty)
421                                 return true;
422
423                         if (value.length > this.length)
424                                 return false;
425
426                         return (0 == Compare (this, length - value.length, value, 0, value.length));
427                 }
428
429                 public int IndexOfAny (char [] anyOf)
430                 {
431                         if (anyOf == null)
432                                 throw new ArgumentNullException ("anyOf");
433
434                         return InternalIndexOfAny (anyOf, 0, this.length);
435                 }
436
437                 public int IndexOfAny (char [] anyOf, int startIndex)
438                 {
439                         if (anyOf == null)
440                                 throw new ArgumentNullException ("anyOf");
441                         if (startIndex < 0 || startIndex >= this.length)
442                                 throw new ArgumentOutOfRangeException ("sourceIndex");
443
444                         return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
445                 }
446
447                 public int IndexOfAny (char [] anyOf, int startIndex, int count)
448                 {
449                         if (anyOf == null)
450                                 throw new ArgumentNullException ("anyOf");
451                         if (startIndex < 0)
452                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
453                         if (count < 0)
454                                 throw new ArgumentOutOfRangeException ("count", "< 0");
455                         // re-ordered to avoid possible integer overflow
456                         if (startIndex > this.length - count)
457                                 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
458
459                         return InternalIndexOfAny (anyOf, startIndex, count);
460                 }
461
462                 public int IndexOf (char value)
463                 {
464                         return IndexOf (value, 0, this.length);
465                 }
466
467                 public int IndexOf (String value)
468                 {
469                         return IndexOf (value, 0, this.length);
470                 }
471
472                 public int IndexOf (char value, int startIndex)
473                 {
474                         return IndexOf (value, startIndex, this.length - startIndex);
475                 }
476
477                 public int IndexOf (String value, int startIndex)
478                 {
479                         return IndexOf (value, startIndex, this.length - startIndex);
480                 }
481
482                 /* This method is culture-insensitive */
483                 public int IndexOf (char value, int startIndex, int count)
484                 {
485                         if (startIndex < 0)
486                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
487                         if (count < 0)
488                                 throw new ArgumentOutOfRangeException ("count", "< 0");
489                         // re-ordered to avoid possible integer overflow
490                         if (startIndex > this.length - count)
491                                 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
492
493                         if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
494                                 return -1;
495
496                         for (int pos = startIndex; pos < startIndex + count; pos++) {
497                                 if (this[pos] == value)
498                                         return(pos);
499                         }
500                         return -1;
501                 }
502
503                 /* But this one is culture-sensitive */
504                 public int IndexOf (String value, int startIndex, int count)
505                 {
506                         if (value == null)
507                                 throw new ArgumentNullException ("value");
508                         if (startIndex < 0)
509                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
510                         if (count < 0)
511                                 throw new ArgumentOutOfRangeException ("count", "< 0");
512                         // re-ordered to avoid possible integer overflow
513                         if (startIndex > this.length - count)
514                                 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
515
516                         if (value.length == 0)
517                                 return startIndex;
518
519                         if (startIndex == 0 && this.length == 0)
520                                 return -1;
521
522                         if (count == 0)
523                                 return -1;
524
525                         return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
526                 }
527
528                 public int LastIndexOfAny (char [] anyOf)
529                 {
530                         if (anyOf == null)
531                                 throw new ArgumentNullException ("anyOf");
532
533                         return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
534                 }
535
536                 public int LastIndexOfAny (char [] anyOf, int startIndex)
537                 {
538                         if (anyOf == null) 
539                                 throw new ArgumentNullException ("anyOf");
540
541                         if (startIndex < 0 || startIndex > this.length)
542                                 throw new ArgumentOutOfRangeException ();
543
544                         if (this.length == 0)
545                                 return -1;
546
547                         return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
548                 }
549
550                 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
551                 {
552                         if (anyOf == null) 
553                                 throw new ArgumentNullException ("anyOf");
554
555                         if ((startIndex < 0) || (startIndex > this.Length))
556                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
557                         if ((count < 0) || (count > this.Length))
558                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
559                         if (startIndex - count + 1 < 0)
560                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
561
562                         if (this.length == 0)
563                                 return -1;
564
565                         return InternalLastIndexOfAny (anyOf, startIndex, count);
566                 }
567
568                 public int LastIndexOf (char value)
569                 {
570                         if (this.length == 0)
571                                 return -1;
572                         else
573                                 return LastIndexOf (value, this.length - 1, this.length);
574                 }
575
576                 public int LastIndexOf (String value)
577                 {
578                         if (this.length == 0)
579                                 /* This overload does additional checking */
580                                 return LastIndexOf (value, 0, 0);
581                         else
582                                 return LastIndexOf (value, this.length - 1, this.length);
583                 }
584
585                 public int LastIndexOf (char value, int startIndex)
586                 {
587                         return LastIndexOf (value, startIndex, startIndex + 1);
588                 }
589
590                 public int LastIndexOf (String value, int startIndex)
591                 {
592                         if (value == null)
593                                 throw new ArgumentNullException ("value");
594                         int max = startIndex;
595                         if (max < this.Length)
596                                 max++;
597                         return LastIndexOf (value, startIndex, max);
598                 }
599
600                 /* This method is culture-insensitive */
601                 public int LastIndexOf (char value, int startIndex, int count)
602                 {
603                         if (startIndex == 0 && this.length == 0)
604                                 return -1;
605
606                         // >= for char (> for string)
607                         if ((startIndex < 0) || (startIndex >= this.Length))
608                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
609                         if ((count < 0) || (count > this.Length))
610                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
611                         if (startIndex - count + 1 < 0)
612                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
613
614                         for(int pos = startIndex; pos > startIndex - count; pos--) {
615                                 if (this [pos] == value)
616                                         return pos;
617                         }
618                         return -1;
619                 }
620
621                 /* But this one is culture-sensitive */
622                 public int LastIndexOf (String value, int startIndex, int count)
623                 {
624                         if (value == null)
625                                 throw new ArgumentNullException ("value");
626                         // -1 > startIndex > for string (0 > startIndex >= for char)
627                         if ((startIndex < -1) || (startIndex > this.Length))
628                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
629                         if ((count < 0) || (count > this.Length))
630                                 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
631                         if (startIndex - count + 1 < 0)
632                                 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
633
634                         if (value == String.Empty)
635                                 return 0;
636
637                         if (startIndex == 0 && this.length == 0)
638                                 return -1;
639
640                         // This check is needed to match undocumented MS behaviour
641                         if (this.length == 0 && value.length > 0)
642                                 return -1;
643
644                         if (value.length > startIndex)
645                                 return -1;
646
647                         if (count == 0)
648                                 return -1;
649
650                         if (startIndex == this.Length)
651                                 startIndex--;
652                         return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
653                 }
654
655                 public String PadLeft (int totalWidth)
656                 {
657                         return PadLeft (totalWidth, ' ');
658                 }
659
660                 public String PadLeft (int totalWidth, char paddingChar)
661                 {
662                         if (totalWidth < 0)
663                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
664
665                         if (totalWidth < this.length)
666                                 return String.Copy (this);
667
668                         return InternalPad (totalWidth, paddingChar, false);
669                 }
670
671                 public String PadRight (int totalWidth)
672                 {
673                         return PadRight (totalWidth, ' ');
674                 }
675
676                 public String PadRight (int totalWidth, char paddingChar)
677                 {
678                         if (totalWidth < 0)
679                                 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
680
681                         if (totalWidth < this.length)
682                                 return String.Copy (this);
683
684                         return InternalPad (totalWidth, paddingChar, true);
685                 }
686
687                 public bool StartsWith (String value)
688                 {
689                         if (value == null)
690                                 throw new ArgumentNullException ("value");
691                         
692                         if (value == String.Empty)
693                                 return true;
694
695                         if (this.length < value.length)
696                                 return false;
697
698                         return (0 == Compare (this, 0, value, 0 , value.length));
699                 }
700
701                 /* This method is culture insensitive */
702                 public String Replace (char oldChar, char newChar)
703                 {
704                         return InternalReplace (oldChar, newChar);
705                 }
706
707                 /* This method is culture sensitive */
708                 public String Replace (String oldValue, String newValue)
709                 {
710                         if (oldValue == null)
711                                 throw new ArgumentNullException ("oldValue");
712
713                         if (oldValue == String.Empty)
714                                 throw new ArgumentException ("oldValue is the empty string.");
715
716                         if (this == String.Empty)
717                                 return this;
718
719                         if (oldValue.Length == 0) {
720                                 return(this);
721                         }
722                         
723                         if (newValue == null)
724                                 newValue = String.Empty;
725
726                         return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
727                 }
728
729                 public String Remove (int startIndex, int count)
730                 {
731                         if (startIndex < 0)
732                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
733                         if (count < 0)
734                                 throw new ArgumentOutOfRangeException ("count", "< 0");
735                         // re-ordered to avoid possible integer overflow
736                         if (startIndex > this.length - count)
737                                 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
738
739                         return InternalRemove (startIndex, count);
740                 }
741
742                 public String ToLower ()
743                 {
744                         // CurrentCulture can never be invariant or null
745                         return InternalToLower (CultureInfo.CurrentCulture);
746                 }
747
748                 public String ToLower (CultureInfo culture)
749                 {
750                         if (culture == null)
751                                 throw new ArgumentNullException ("culture");
752
753                         if (culture.LCID == 0x007F) { // Invariant
754                                 return ToLowerInvariant ();
755                         }
756                         return InternalToLower (culture);
757                 }
758
759                 internal unsafe String ToLowerInvariant ()
760                 {
761                         string tmp = InternalAllocateStr (length);
762                         fixed (char* source = &start_char, dest = tmp) {
763
764                                 char* destPtr = (char*)dest;
765                                 char* sourcePtr = (char*)source;
766
767                                 for (int n = 0; n < length; n++) {
768                                         *destPtr = Char.ToLowerInvariant (*sourcePtr);
769                                         sourcePtr++;
770                                         destPtr++;
771                                 }
772                         }
773                         return tmp;
774                 }
775
776                 public String ToUpper ()
777                 {
778                         // CurrentCulture can never be invariant or null
779                         return InternalToUpper (CultureInfo.CurrentCulture);
780                 }
781
782                 public String ToUpper (CultureInfo culture)
783                 {
784                         if (culture == null)
785                                 throw new ArgumentNullException ("culture");
786
787                         if (culture.LCID == 0x007F) { // Invariant
788                                 return ToUpperInvariant ();
789                         }
790                         return InternalToUpper (culture);
791                 }
792
793                 internal unsafe String ToUpperInvariant ()
794                 {
795                         string tmp = InternalAllocateStr (length);
796                         fixed (char* source = &start_char, dest = tmp) {
797
798                                 char* destPtr = (char*)dest;
799                                 char* sourcePtr = (char*)source;
800
801                                 for (int n = 0; n < length; n++) {
802                                         *destPtr = Char.ToUpperInvariant (*sourcePtr);
803                                         sourcePtr++;
804                                         destPtr++;
805                                 }
806                         }
807                         return tmp;
808                 }
809
810                 public override String ToString ()
811                 {
812                         return this;
813                 }
814
815                 public String ToString (IFormatProvider provider)
816                 {
817                         return this;
818                 }
819
820                 public String Trim ()
821                 {
822                         return Trim (null);
823                 }
824
825                 public static String Format (String format, Object arg0)
826                 {
827                         return Format (null, format, new Object[] {arg0});
828                 }
829
830                 public static String Format (String format, Object arg0, Object arg1)
831                 {
832                         return Format (null, format, new Object[] {arg0, arg1});
833                 }
834
835                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
836                 {
837                         return Format (null, format, new Object[] {arg0, arg1, arg2});
838                 }
839
840                 public static string Format (string format, params object[] args)
841                 {
842                         return Format (null, format, args);
843                 }
844         
845                 public static string Format (IFormatProvider provider, string format, params object[] args)
846                 {
847                         StringBuilder b = new StringBuilder ();
848                         FormatHelper (b, provider, format, args);
849                         return b.ToString ();
850                 }
851                 
852                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
853                 {
854                         if (format == null || args == null)
855                                 throw new ArgumentNullException ();
856
857                         int ptr = 0;
858                         int start = ptr;
859                         while (ptr < format.length) {
860                                 char c = format[ptr ++];
861
862                                 if (c == '{') {
863                                         result.Append (format, start, ptr - start - 1);
864
865                                         // check for escaped open bracket
866
867                                         if (format[ptr] == '{') {
868                                                 start = ptr ++;
869                                                 continue;
870                                         }
871
872                                         // parse specifier
873                                 
874                                         int n, width;
875                                         bool left_align;
876                                         string arg_format;
877
878                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
879                                         if (n >= args.Length)
880                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
881
882                                         // format argument
883
884                                         object arg = args[n];
885
886                                         string str;
887                                         if (arg == null)
888                                                 str = "";
889                                         else if (arg is IFormattable)
890                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
891                                         else
892                                                 str = arg.ToString ();
893
894                                         // pad formatted string and append to result
895
896                                         if (width > str.length) {
897                                                 string pad = new String (' ', width - str.length);
898
899                                                 if (left_align) {
900                                                         result.Append (str);
901                                                         result.Append (pad);
902                                                 }
903                                                 else {
904                                                         result.Append (pad);
905                                                         result.Append (str);
906                                                 }
907                                         }
908                                         else
909                                                 result.Append (str);
910
911                                         start = ptr;
912                                 }
913                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
914                                         result.Append (format, start, ptr - start - 1);
915                                         start = ptr ++;
916                                 }
917                                 else if (c == '}') {
918                                         throw new FormatException ("Input string was not in a correct format.");
919                                 }
920                         }
921
922                         if (start < format.length)
923                                 result.Append (format.Substring (start));
924                 }
925
926                 public static String Copy (String str)
927                 {
928                         if (str == null)
929                                 throw new ArgumentNullException ("str");
930
931                         int length = str.length;
932
933                         String tmp = InternalAllocateStr (length);
934                         InternalStrcpy (tmp, 0, str);
935                         return tmp;
936                 }
937
938                 public static String Concat (Object obj)
939                 {
940                         if (obj == null)
941                                 return String.Empty;
942
943                         return obj.ToString ();
944                 }
945
946                 public static String Concat (Object obj1, Object obj2)
947                 {
948                         string s1, s2;
949
950                         s1 = (obj1 != null) ? obj1.ToString () : null;
951                         s2 = (obj2 != null) ? obj2.ToString () : null;
952                         
953                         if (s1 == null) {
954                                 if (s2 == null)
955                                         return String.Empty;
956                                 else
957                                         return s2;
958                         } else if (s2 == null)
959                                 return s1;
960
961                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
962                         InternalStrcpy (tmp, 0, s1);
963                         InternalStrcpy (tmp, s1.length, s2);
964
965                         return tmp;
966                 }
967
968                 public static String Concat (Object obj1, Object obj2, Object obj3)
969                 {
970                         string s1, s2, s3;
971                         if (obj1 == null)
972                                 s1 = String.Empty;
973                         else
974                                 s1 = obj1.ToString ();
975
976                         if (obj2 == null)
977                                 s2 = String.Empty;
978                         else
979                                 s2 = obj2.ToString ();
980
981                         if (obj3 == null)
982                                 s3 = String.Empty;
983                         else
984                                 s3 = obj3.ToString ();
985
986                         return Concat (s1, s2, s3);
987                 }
988
989 #if ! BOOTSTRAP_WITH_OLDLIB
990                 [CLSCompliant(false)]
991                 public static String Concat (Object obj1, Object obj2, Object obj3,
992                                              Object obj4, __arglist)
993                 {
994                         string s1, s2, s3, s4;
995
996                         if (obj1 == null)
997                                 s1 = String.Empty;
998                         else
999                                 s1 = obj1.ToString ();
1000
1001                         if (obj2 == null)
1002                                 s2 = String.Empty;
1003                         else
1004                                 s2 = obj2.ToString ();
1005
1006                         if (obj3 == null)
1007                                 s3 = String.Empty;
1008                         else
1009                                 s3 = obj3.ToString ();
1010
1011                         ArgIterator iter = new ArgIterator (__arglist);
1012                         int argCount = iter.GetRemainingCount();
1013
1014                         StringBuilder sb = new StringBuilder ();
1015                         if (obj4 != null)
1016                                 sb.Append (obj4.ToString ());
1017
1018                         for (int i = 0; i < argCount; i++) {
1019                                 TypedReference typedRef = iter.GetNextArg ();
1020                                 sb.Append (TypedReference.ToObject (typedRef));
1021                         }
1022
1023                         s4 = sb.ToString ();
1024
1025                         return Concat (s1, s2, s3, s4);                 
1026                 }
1027 #endif
1028
1029                 public static String Concat (String s1, String s2)
1030                 {
1031                         if (s1 == null) {
1032                                 if (s2 == null)
1033                                         return String.Empty;
1034                                 return s2;
1035                         }
1036
1037                         if (s2 == null)
1038                                 return s1; 
1039
1040                         String tmp = InternalAllocateStr (s1.length + s2.length);
1041
1042                         InternalStrcpy (tmp, 0, s1);
1043                         InternalStrcpy (tmp, s1.length, s2);
1044
1045                         return tmp;
1046                 }
1047
1048                 public static String Concat (String s1, String s2, String s3)
1049                 {
1050                         if (s1 == null){
1051                                 if (s2 == null){
1052                                         if (s3 == null)
1053                                                 return String.Empty;
1054                                         return s3;
1055                                 } else {
1056                                         if (s3 == null)
1057                                                 return s2;
1058                                 }
1059                                 s1 = String.Empty;
1060                         } else {
1061                                 if (s2 == null){
1062                                         if (s3 == null)
1063                                                 return s1;
1064                                         else
1065                                                 s2 = String.Empty;
1066                                 } else {
1067                                         if (s3 == null)
1068                                                 s3 = String.Empty;
1069                                 }
1070                         }
1071
1072                         //return InternalConcat (s1, s2, s3);
1073                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1074
1075                         InternalStrcpy (tmp, 0, s1);
1076                         InternalStrcpy (tmp, s1.length, s2);
1077                         InternalStrcpy (tmp, s1.length + s2.length, s3);
1078
1079                         return tmp;
1080                 }
1081
1082                 public static String Concat (String s1, String s2, String s3, String s4)
1083                 {
1084                         if (s1 == null && s2 == null && s3 == null && s4 == null)
1085                                 return String.Empty;
1086
1087                         if (s1 == null)
1088                                 s1 = String.Empty;
1089                         if (s2 == null)
1090                                 s2 = String.Empty;
1091                         if (s3 == null)
1092                                 s3 = String.Empty;
1093                         if (s4 == null)
1094                                 s4 = String.Empty;
1095
1096                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1097
1098                         InternalStrcpy (tmp, 0, s1);
1099                         InternalStrcpy (tmp, s1.length, s2);
1100                         InternalStrcpy (tmp, s1.length + s2.length, s3);
1101                         InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
1102
1103                         return tmp;
1104                 }
1105
1106                 public static String Concat (params Object[] args)
1107                 {
1108                         if (args == null)
1109                                 throw new ArgumentNullException ("args");
1110
1111                         int i = args.Length;
1112                         if (i == 0)
1113                                 return String.Empty;
1114
1115                         string [] strings = new string [i];
1116                         i = 0;
1117                         int len = 0;
1118                         foreach (object arg in args) {
1119                                 if (arg == null) {
1120                                         strings[i] = String.Empty;
1121                                 } else {
1122                                         strings[i] = arg.ToString ();
1123                                         len += strings[i].length;
1124                                 }
1125                                 i++;
1126                         }
1127
1128                         if (len == 0)
1129                                 return String.Empty;
1130
1131                         return InternalJoin (String.Empty, strings, 0, strings.Length);
1132                 }
1133
1134                 public static String Concat (params String[] values)
1135                 {
1136                         if (values == null)
1137                                 throw new ArgumentNullException ("values");
1138
1139                         return InternalJoin (String.Empty, values, 0, values.Length);
1140                 }
1141
1142                 public String Insert (int startIndex, String value)
1143                 {
1144                         if (value == null)
1145                                 throw new ArgumentNullException ("value");
1146
1147                         if (startIndex < 0 || startIndex > this.length)
1148                                 throw new ArgumentOutOfRangeException ();
1149
1150                         return InternalInsert (startIndex, value);
1151                 }
1152
1153
1154                 public static string Intern (string str)
1155                 {
1156                         if (str == null)
1157                                 throw new ArgumentNullException ("str");
1158
1159                         return InternalIntern (str);
1160                 }
1161
1162                 public static string IsInterned (string str)
1163                 {
1164                         if (str == null)
1165                                 throw new ArgumentNullException ("str");
1166
1167                         return InternalIsInterned (str);
1168                 }
1169         
1170                 public static string Join (string separator, string [] value)
1171                 {
1172                         if (value == null)
1173                                 throw new ArgumentNullException ("value");
1174
1175                         return Join (separator, value, 0, value.Length);
1176                 }
1177
1178                 public static string Join (string separator, string[] value, int startIndex, int count)
1179                 {
1180                         if (value == null)
1181                                 throw new ArgumentNullException ("value");
1182                         if (startIndex < 0)
1183                                 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1184                         if (count < 0)
1185                                 throw new ArgumentOutOfRangeException ("count", "< 0");
1186                         // re-ordered to avoid possible integer overflow
1187                         if (startIndex > value.Length - count)
1188                                 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1189
1190                         if (startIndex == value.Length)
1191                                 return String.Empty;
1192                         if (separator == null)
1193                                 separator = String.Empty;
1194
1195                         return InternalJoin (separator, value, startIndex, count);
1196                 }
1197
1198                 bool IConvertible.ToBoolean (IFormatProvider provider)
1199                 {
1200                         return Convert.ToBoolean (this, provider);
1201                 }
1202
1203                 byte IConvertible.ToByte (IFormatProvider provider)
1204                 {
1205                         return Convert.ToByte (this, provider);
1206                 }
1207
1208                 char IConvertible.ToChar (IFormatProvider provider)
1209                 {
1210                         return Convert.ToChar (this, provider);
1211                 }
1212
1213                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1214                 {
1215                         return Convert.ToDateTime (this, provider);
1216                 }
1217
1218                 decimal IConvertible.ToDecimal (IFormatProvider provider)
1219                 {
1220                         return Convert.ToDecimal (this, provider);
1221                 }
1222
1223                 double IConvertible.ToDouble (IFormatProvider provider)
1224                 {
1225                         return Convert.ToDouble (this, provider);
1226                 }
1227
1228                 short IConvertible.ToInt16 (IFormatProvider provider)
1229                 {
1230                         return Convert.ToInt16 (this, provider);
1231                 }
1232
1233                 int IConvertible.ToInt32 (IFormatProvider provider)
1234                 {
1235                         return Convert.ToInt32 (this, provider);
1236                 }
1237
1238                 long IConvertible.ToInt64 (IFormatProvider provider)
1239                 {
1240                         return Convert.ToInt64 (this, provider);
1241                 }
1242         
1243                 sbyte IConvertible.ToSByte (IFormatProvider provider)
1244                 {
1245                         return Convert.ToSByte (this, provider);
1246                 }
1247
1248                 float IConvertible.ToSingle (IFormatProvider provider)
1249                 {
1250                         return Convert.ToSingle (this, provider);
1251                 }
1252
1253                 string IConvertible.ToString (IFormatProvider format)
1254                 {
1255                         return this;
1256                 }
1257
1258                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1259                 {
1260                         return Convert.ToType (this, conversionType,  provider);
1261                 }
1262
1263                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1264                 {
1265                         return Convert.ToUInt16 (this, provider);
1266                 }
1267
1268                 uint IConvertible.ToUInt32 (IFormatProvider provider)
1269                 {
1270                         return Convert.ToUInt32 (this, provider);
1271                 }
1272
1273                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1274                 {
1275                         return Convert.ToUInt64 (this, provider);
1276                 }
1277
1278                 TypeCode IConvertible.GetTypeCode ()
1279                 {
1280                         return TypeCode.String;
1281                 }
1282
1283                 public int Length {
1284                         get {
1285                                 return length;
1286                         }
1287                 }
1288
1289                 public CharEnumerator GetEnumerator ()
1290                 {
1291                         return new CharEnumerator (this);
1292                 }
1293
1294                 IEnumerator IEnumerable.GetEnumerator ()
1295                 {
1296                         return new CharEnumerator (this);
1297                 }
1298
1299                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1300                                                           out bool left_align, out string format)
1301                 {
1302                         // parses format specifier of form:
1303                         //   N,[\ +[-]M][:F]}
1304                         //
1305                         // where:
1306
1307                         try {
1308                                 // N = argument number (non-negative integer)
1309
1310                                 n = ParseDecimal (str, ref ptr);
1311                                 if (n < 0)
1312                                         throw new FormatException ("Input string was not in a correct format.");
1313
1314                                 // M = width (non-negative integer)
1315
1316                                 if (str[ptr] == ',') {
1317                                         // White space between ',' and number or sign.
1318                                         int start = ++ptr;
1319                                         while (Char.IsWhiteSpace (str [ptr]))
1320                                                 ++ptr;
1321
1322                                         format = str.Substring (start, ptr - start);
1323
1324                                         left_align = (str [ptr] == '-');
1325                                         if (left_align)
1326                                                 ++ ptr;
1327
1328                                         width = ParseDecimal (str, ref ptr);
1329                                         if (width < 0)
1330                                                 throw new FormatException ("Input string was not in a correct format.");
1331                                 }
1332                                 else {
1333                                         width = 0;
1334                                         left_align = false;
1335                                         format = "";
1336                                 }
1337
1338                                 // F = argument format (string)
1339
1340                                 if (str[ptr] == ':') {
1341                                         int start = ++ ptr;
1342                                         while (str[ptr] != '}')
1343                                                 ++ ptr;
1344
1345                                         format += str.Substring (start, ptr - start);
1346                                 }
1347                                 else
1348                                         format = null;
1349
1350                                 if (str[ptr ++] != '}')
1351                                         throw new FormatException ("Input string was not in a correct format.");
1352                         }
1353                         catch (IndexOutOfRangeException) {
1354                                 throw new FormatException ("Input string was not in a correct format.");
1355                         }
1356                 }
1357
1358                 private static int ParseDecimal (string str, ref int ptr)
1359                 {
1360                         int p = ptr;
1361                         int n = 0;
1362                         while (true) {
1363                                 char c = str[p];
1364                                 if (c < '0' || '9' < c)
1365                                         break;
1366
1367                                 n = n * 10 + c - '0';
1368                                 ++ p;
1369                         }
1370
1371                         if (p == ptr)
1372                                 return -1;
1373
1374                         ptr = p;
1375                         return n;
1376                 }
1377
1378                 internal unsafe void InternalSetChar (int idx, char val)
1379                 {
1380                         if ((uint) idx >= (uint) Length)
1381                                 throw new ArgumentOutOfRangeException ("idx");
1382
1383                         fixed (char * pStr = &start_char) 
1384                         {
1385                                 pStr [idx] = val;
1386                         }
1387                 }
1388
1389                 internal unsafe void InternalSetLength (int newLength)
1390                 {
1391                         if (newLength > length)
1392                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1393
1394                         length = newLength;
1395
1396                         // zero terminate, we can pass string objects directly via pinvoke
1397                         fixed (char * pStr = &start_char) {
1398                                 pStr [length] = '\0';
1399                         }
1400                 }
1401
1402                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1403                 unsafe public extern String (char *value);
1404
1405                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1406                 unsafe public extern String (char *value, int startIndex, int length);
1407
1408                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1409                 unsafe public extern String (sbyte *value);
1410
1411                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1412                 unsafe public extern String (sbyte *value, int startIndex, int length);
1413
1414                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1415                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1416
1417                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1418                 public extern String (char [] val, int startIndex, int length);
1419
1420                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1421                 public extern String (char [] val);
1422
1423                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1424                 public extern String (char c, int count);
1425
1426                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1427                 public extern override int GetHashCode ();
1428
1429                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1430                 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1431
1432                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1433                 private extern String InternalInsert (int sourceIndex, String value);
1434
1435                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1436                 private extern String InternalReplace (char oldChar, char newChar);
1437
1438                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1439                 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1440
1441                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1442                 private extern String InternalRemove (int sIndex, int count);
1443
1444                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1445                 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1446
1447                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1448                 private extern String[] InternalSplit (char[] separator, int count);
1449
1450                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1451                 private extern String InternalTrim (char[] chars, int typ);
1452
1453                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1454                 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1455
1456                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1457                 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1458
1459                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1460                 private extern String InternalPad (int width, char chr, bool right);
1461
1462                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1463                 private extern String InternalToLower (CultureInfo culture);
1464
1465                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1466                 private extern String InternalToUpper (CultureInfo culture);
1467
1468                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1469                 internal extern static String InternalAllocateStr (int length);
1470
1471                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1472                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1473
1474                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1475                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1476
1477                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1478                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1479
1480                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1481                 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1482
1483                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1484                 private extern static string InternalIntern (string str);
1485
1486                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1487                 private extern static string InternalIsInterned (string str);
1488         }
1489 }