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