2004-03-10 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 (newValue == null)
660                                 newValue = String.Empty;
661
662                         return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
663                 }
664
665                 public String Remove (int startIndex, int count)
666                 {
667                         if (startIndex < 0 || count < 0 || startIndex + count > this.length)
668                                 throw new ArgumentOutOfRangeException ();
669
670                         return InternalRemove (startIndex, count);
671                 }
672
673                 public String ToLower ()
674                 {
675                         return InternalToLower (CultureInfo.CurrentCulture);
676                 }
677
678                 public String ToLower (CultureInfo culture)
679                 {
680                         return InternalToLower (culture);
681                 }
682
683                 public String ToUpper ()
684                 {
685                         return InternalToUpper (CultureInfo.CurrentCulture);
686                 }
687
688                 public String ToUpper (CultureInfo culture)
689                 {
690                         return InternalToUpper (culture);
691                 }
692
693                 public override String ToString ()
694                 {
695                         return this;
696                 }
697
698                 public String ToString (IFormatProvider provider)
699                 {
700                         return this;
701                 }
702
703                 public String Trim ()
704                 {
705                         return Trim (null);
706                 }
707
708                 public static String Format (String format, Object arg0)
709                 {
710                         return Format (null, format, new Object[] {arg0});
711                 }
712
713                 public static String Format (String format, Object arg0, Object arg1)
714                 {
715                         return Format (null, format, new Object[] {arg0, arg1});
716                 }
717
718                 public static String Format (String format, Object arg0, Object arg1, Object arg2)
719                 {
720                         return Format (null, format, new Object[] {arg0, arg1, arg2});
721                 }
722
723                 public static string Format (string format, params object[] args)
724                 {
725                         return Format (null, format, args);
726                 }
727         
728                 public static string Format (IFormatProvider provider, string format, params object[] args)
729                 {
730                         StringBuilder b = new StringBuilder ();
731                         FormatHelper (b, provider, format, args);
732                         return b.ToString ();
733                 }
734                 
735                 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
736                 {
737                         if (format == null || args == null)
738                                 throw new ArgumentNullException ();
739
740                         int ptr = 0;
741                         int start = ptr;
742                         while (ptr < format.length) {
743                                 char c = format[ptr ++];
744
745                                 if (c == '{') {
746                                         result.Append (format, start, ptr - start - 1);
747
748                                         // check for escaped open bracket
749
750                                         if (format[ptr] == '{') {
751                                                 start = ptr ++;
752                                                 continue;
753                                         }
754
755                                         // parse specifier
756                                 
757                                         int n, width;
758                                         bool left_align;
759                                         string arg_format;
760
761                                         ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
762                                         if (n >= args.Length)
763                                                 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
764
765                                         // format argument
766
767                                         object arg = args[n];
768
769                                         string str;
770                                         if (arg == null)
771                                                 str = "";
772                                         else if (arg is IFormattable)
773                                                 str = ((IFormattable)arg).ToString (arg_format, provider);
774                                         else
775                                                 str = arg.ToString ();
776
777                                         // pad formatted string and append to result
778
779                                         if (width > str.length) {
780                                                 string pad = new String (' ', width - str.length);
781
782                                                 if (left_align) {
783                                                         result.Append (str);
784                                                         result.Append (pad);
785                                                 }
786                                                 else {
787                                                         result.Append (pad);
788                                                         result.Append (str);
789                                                 }
790                                         }
791                                         else
792                                                 result.Append (str);
793
794                                         start = ptr;
795                                 }
796                                 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
797                                         result.Append (format, start, ptr - start - 1);
798                                         start = ptr ++;
799                                 }
800                                 else if (c == '}') {
801                                         throw new FormatException ("Input string was not in a correct format.");
802                                 }
803                         }
804
805                         if (start < format.length)
806                                 result.Append (format.Substring (start));
807                 }
808
809                 public static String Copy (String str)
810                 {
811                         if (str == null)
812                                 throw new ArgumentNullException ("str");
813
814                         int length = str.length;
815
816                         String tmp = InternalAllocateStr (length);
817                         InternalStrcpy (tmp, 0, str);
818                         return tmp;
819                 }
820
821                 public static String Concat (Object obj)
822                 {
823                         if (obj == null)
824                                 return String.Empty;
825
826                         return obj.ToString ();
827                 }
828
829                 public static String Concat (Object obj1, Object obj2)
830                 {
831                         string s1, s2;
832
833                         s1 = (obj1 != null) ? obj1.ToString () : null;
834                         s2 = (obj2 != null) ? obj2.ToString () : null;
835                         
836                         if (s1 == null) {
837                                 if (s2 == null)
838                                         return String.Empty;
839                                 else
840                                         return s2;
841                         } else if (s2 == null)
842                                 return s1;
843
844                         String tmp = InternalAllocateStr (s1.Length + s2.Length);
845                         InternalStrcpy (tmp, 0, s1);
846                         InternalStrcpy (tmp, s1.length, s2);
847
848                         return tmp;
849                 }
850
851                 public static String Concat (Object obj1, Object obj2, Object obj3)
852                 {
853                         string s1, s2, s3;
854                         if (obj1 == null)
855                                 s1 = String.Empty;
856                         else
857                                 s1 = obj1.ToString ();
858
859                         if (obj2 == null)
860                                 s2 = String.Empty;
861                         else
862                                 s2 = obj2.ToString ();
863
864                         if (obj3 == null)
865                                 s3 = String.Empty;
866                         else
867                                 s3 = obj3.ToString ();
868
869                         return Concat (s1, s2, s3);
870                 }
871
872                 public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
873                 {
874                         string s1, s2, s3, s4;
875
876                         if (obj1 == null)
877                                 s1 = String.Empty;
878                         else
879                                 s1 = obj1.ToString ();
880
881                         if (obj2 == null)
882                                 s2 = String.Empty;
883                         else
884                                 s2 = obj2.ToString ();
885
886                         if (obj3 == null)
887                                 s3 = String.Empty;
888                         else
889                                 s3 = obj3.ToString ();
890
891                         if (obj4 == null)
892                                 s4 = String.Empty;
893                         else
894                                 s4 = obj4.ToString ();
895
896                         return Concat (s1, s2, s3, s4);
897                         
898                 }
899
900                 public static String Concat (String s1, String s2)
901                 {
902                         if (s1 == null) {
903                                 if (s2 == null)
904                                         return String.Empty;
905                                 return s2;
906                         }
907
908                         if (s2 == null)
909                                 return s1; 
910
911                         String tmp = InternalAllocateStr (s1.length + s2.length);
912
913                         InternalStrcpy (tmp, 0, s1);
914                         InternalStrcpy (tmp, s1.length, s2);
915
916                         return tmp;
917                 }
918
919                 public static String Concat (String s1, String s2, String s3)
920                 {
921                         if (s1 == null){
922                                 if (s2 == null){
923                                         if (s3 == null)
924                                                 return String.Empty;
925                                         return s3;
926                                 } else {
927                                         if (s3 == null)
928                                                 return s2;
929                                 }
930                                 s1 = String.Empty;
931                         } else {
932                                 if (s2 == null){
933                                         if (s3 == null)
934                                                 return s1;
935                                         else
936                                                 s2 = String.Empty;
937                                 } else {
938                                         if (s3 == null)
939                                                 s3 = String.Empty;
940                                 }
941                         }
942
943                         //return InternalConcat (s1, s2, s3);
944                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
945
946                         InternalStrcpy (tmp, 0, s1);
947                         InternalStrcpy (tmp, s1.length, s2);
948                         InternalStrcpy (tmp, s1.length + s2.length, s3);
949
950                         return tmp;
951                 }
952
953                 public static String Concat (String s1, String s2, String s3, String s4)
954                 {
955                         if (s1 == null && s2 == null && s3 == null && s4 == null)
956                                 return String.Empty;
957
958                         if (s1 == null)
959                                 s1 = String.Empty;
960                         if (s2 == null)
961                                 s2 = String.Empty;
962                         if (s3 == null)
963                                 s3 = String.Empty;
964                         if (s4 == null)
965                                 s4 = String.Empty;
966
967                         String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
968
969                         InternalStrcpy (tmp, 0, s1);
970                         InternalStrcpy (tmp, s1.length, s2);
971                         InternalStrcpy (tmp, s1.length + s2.length, s3);
972                         InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
973
974                         return tmp;
975                 }
976
977                 public static String Concat (params Object[] args)
978                 {
979                         string [] strings;
980                         int len, i, currentpos;
981
982                         if (args == null)
983                                 throw new ArgumentNullException ("args");
984
985                         strings = new string [args.Length];
986                         len = 0;
987                         i = 0;
988                         foreach (object arg in args) {
989                                 /* use Empty for each null argument */
990                                 if (arg == null)
991                                         strings[i] = String.Empty;
992                                 else
993                                         strings[i] = arg.ToString ();
994                                 len += strings[i].length;
995                                 i++;
996                         }
997
998                         if (len == 0)
999                                 return String.Empty;
1000
1001                         currentpos = 0;
1002
1003                         String tmp = InternalAllocateStr (len);
1004                         for (i = 0; i < strings.Length; i++) {
1005                                 InternalStrcpy (tmp, currentpos, strings[i]);
1006                                 currentpos += strings[i].length;
1007                         }
1008
1009                         return tmp;
1010                 }
1011
1012                 public static String Concat (params String[] values)
1013                 {
1014                         int len, i, currentpos;
1015
1016                         if (values == null)
1017                                 throw new ArgumentNullException ("values");
1018
1019                         len = 0;
1020                         foreach (string value in values)
1021                                 len += value != null ? value.length : 0;
1022
1023                         if (len == 0)
1024                                 return String.Empty;
1025
1026                         currentpos = 0;
1027
1028                         String tmp = InternalAllocateStr (len);
1029                         for (i = 0; i < values.Length; i++) {
1030                                 if (values[i] == null)
1031                                         continue;
1032
1033                                 InternalStrcpy (tmp, currentpos, values[i]);
1034                                 currentpos += values[i].length;
1035                         }       
1036
1037                         return tmp;
1038                 }
1039
1040                 public String Insert (int startIndex, String value)
1041                 {
1042                         if (value == null)
1043                                 throw new ArgumentNullException ("value");
1044
1045                         if (startIndex < 0 || startIndex > this.length)
1046                                 throw new ArgumentOutOfRangeException ();
1047
1048                         return InternalInsert (startIndex, value);
1049                 }
1050
1051
1052                 public static string Intern (string str)
1053                 {
1054                         if (str == null)
1055                                 throw new ArgumentNullException ("str");
1056
1057                         return InternalIntern (str);
1058                 }
1059
1060                 public static string IsInterned (string str)
1061                 {
1062                         if (str == null)
1063                                 throw new ArgumentNullException ("str");
1064
1065                         return InternalIsInterned (str);
1066                 }
1067         
1068                 public static string Join (string separator, string [] value)
1069                 {
1070                         if (value == null)
1071                                 throw new ArgumentNullException ("value");
1072
1073                         return Join (separator, value, 0, value.Length);
1074                 }
1075
1076                 public static string Join (string separator, string[] value, int startIndex, int count)
1077                 {
1078                         if (value == null)
1079                                 throw new ArgumentNullException ("value");
1080
1081                         if (startIndex + count > value.Length)
1082                                 throw new ArgumentOutOfRangeException ();
1083
1084                         if (startIndex == value.Length)
1085                                 return String.Empty;
1086
1087                         return InternalJoin (separator, value, startIndex, count);
1088                 }
1089
1090                 bool IConvertible.ToBoolean (IFormatProvider provider)
1091                 {
1092                         return Convert.ToBoolean (this);
1093                 }
1094
1095                 byte IConvertible.ToByte (IFormatProvider provider)
1096                 {
1097                         return Convert.ToByte (this);
1098                 }
1099
1100                 char IConvertible.ToChar (IFormatProvider provider)
1101                 {
1102                         return Convert.ToChar (this);
1103                 }
1104
1105                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1106                 {
1107                         return Convert.ToDateTime (this);
1108                 }
1109
1110                 decimal IConvertible.ToDecimal (IFormatProvider provider)
1111                 {
1112                         return Convert.ToDecimal (this);
1113                 }
1114
1115                 double IConvertible.ToDouble (IFormatProvider provider)
1116                 {
1117                         return Convert.ToDouble (this);
1118                 }
1119
1120                 short IConvertible.ToInt16 (IFormatProvider provider)
1121                 {
1122                         return Convert.ToInt16 (this);
1123                 }
1124
1125                 int IConvertible.ToInt32 (IFormatProvider provider)
1126                 {
1127                         return Convert.ToInt32 (this);
1128                 }
1129
1130                 long IConvertible.ToInt64 (IFormatProvider provider)
1131                 {
1132                         return Convert.ToInt64 (this);
1133                 }
1134         
1135                 [CLSCompliant (false)]
1136                 sbyte IConvertible.ToSByte (IFormatProvider provider)
1137                 {
1138                         return Convert.ToSByte (this);
1139                 }
1140
1141                 float IConvertible.ToSingle (IFormatProvider provider)
1142                 {
1143                         return Convert.ToSingle (this);
1144                 }
1145
1146                 string IConvertible.ToString (IFormatProvider format)
1147                 {
1148                         return this;
1149                 }
1150
1151                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1152                 {
1153                         return Convert.ToType (this, conversionType,  provider);
1154                 }
1155
1156                 [CLSCompliant (false)]
1157                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1158                 {
1159                         return Convert.ToUInt16 (this);
1160                 }
1161
1162                 [CLSCompliant (false)]
1163                 uint IConvertible.ToUInt32 (IFormatProvider provider)
1164                 {
1165                         return Convert.ToUInt32 (this);
1166                 }
1167
1168                 [CLSCompliant (false)]
1169                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1170                 {
1171                         return Convert.ToUInt64 (this);
1172                 }
1173
1174                 TypeCode IConvertible.GetTypeCode ()
1175                 {
1176                         return TypeCode.String;
1177                 }
1178
1179                 public int Length {
1180                         get {
1181                                 return length;
1182                         }
1183                 }
1184
1185                 public CharEnumerator GetEnumerator ()
1186                 {
1187                         return new CharEnumerator (this);
1188                 }
1189
1190                 IEnumerator IEnumerable.GetEnumerator ()
1191                 {
1192                         return new CharEnumerator (this);
1193                 }
1194
1195                 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1196                                                           out bool left_align, out string format)
1197                 {
1198                         // parses format specifier of form:
1199                         //   N,[\ +[-]M][:F]}
1200                         //
1201                         // where:
1202
1203                         try {
1204                                 // N = argument number (non-negative integer)
1205
1206                                 n = ParseDecimal (str, ref ptr);
1207                                 if (n < 0)
1208                                         throw new FormatException ("Input string was not in a correct format.");
1209
1210                                 // M = width (non-negative integer)
1211
1212                                 if (str[ptr] == ',') {
1213                                         // White space between ',' and number or sign.
1214                                         int start = ++ptr;
1215                                         while (Char.IsWhiteSpace (str [ptr]))
1216                                                 ++ptr;
1217
1218                                         format = str.Substring (start, ptr - start);
1219
1220                                         left_align = (str [ptr] == '-');
1221                                         if (left_align)
1222                                                 ++ ptr;
1223
1224                                         width = ParseDecimal (str, ref ptr);
1225                                         if (width < 0)
1226                                                 throw new FormatException ("Input string was not in a correct format.");
1227                                 }
1228                                 else {
1229                                         width = 0;
1230                                         left_align = false;
1231                                         format = "";
1232                                 }
1233
1234                                 // F = argument format (string)
1235
1236                                 if (str[ptr] == ':') {
1237                                         int start = ++ ptr;
1238                                         while (str[ptr] != '}')
1239                                                 ++ ptr;
1240
1241                                         format += str.Substring (start, ptr - start);
1242                                 }
1243                                 else
1244                                         format = null;
1245
1246                                 if (str[ptr ++] != '}')
1247                                         throw new FormatException ("Input string was not in a correct format.");
1248                         }
1249                         catch (IndexOutOfRangeException) {
1250                                 throw new FormatException ("Input string was not in a correct format.");
1251                         }
1252                 }
1253
1254                 private static int ParseDecimal (string str, ref int ptr)
1255                 {
1256                         int p = ptr;
1257                         int n = 0;
1258                         while (true) {
1259                                 char c = str[p];
1260                                 if (c < '0' || '9' < c)
1261                                         break;
1262
1263                                 n = n * 10 + c - '0';
1264                                 ++ p;
1265                         }
1266
1267                         if (p == ptr)
1268                                 return -1;
1269
1270                         ptr = p;
1271                         return n;
1272                 }
1273
1274                 internal unsafe void InternalSetChar (int idx, char val)
1275                 {
1276                         if ((uint) idx >= (uint) Length)
1277                                 throw new ArgumentOutOfRangeException ("idx");
1278
1279                         fixed (char * pStr = &start_char) 
1280                         {
1281                                 pStr [idx] = val;
1282                         }
1283                 }
1284
1285                 internal unsafe void InternalSetLength (int newLength)
1286                 {
1287                         if (newLength > length)
1288                                 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1289
1290                         length = newLength;
1291
1292                         // zero terminate, we can pass string objects directly via pinvoke
1293                         fixed (char * pStr = &start_char) {
1294                                 pStr [length] = '\0';
1295                         }
1296                 }
1297
1298                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1299                 unsafe public extern String (char *value);
1300
1301                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1302                 unsafe public extern String (char *value, int startIndex, int length);
1303
1304                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1305                 unsafe public extern String (sbyte *value);
1306
1307                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1308                 unsafe public extern String (sbyte *value, int startIndex, int length);
1309
1310                 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1311                 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1312
1313                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1314                 public extern String (char [] val, int startIndex, int length);
1315
1316                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1317                 public extern String (char [] val);
1318
1319                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1320                 public extern String (char c, int count);
1321
1322                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1323                 public extern override int GetHashCode ();
1324
1325                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1326                 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1327
1328                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1329                 private extern String InternalInsert (int sourceIndex, String value);
1330
1331                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1332                 private extern String InternalReplace (char oldChar, char newChar);
1333
1334                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1335                 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1336
1337                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1338                 private extern String InternalRemove (int sIndex, int count);
1339
1340                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1341                 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1342
1343                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1344                 private extern String[] InternalSplit (char[] separator, int count);
1345
1346                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1347                 private extern String InternalTrim (char[] chars, int typ);
1348
1349                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1350                 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1351
1352                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1353                 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1354
1355                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1356                 private extern String InternalPad (int width, char chr, bool right);
1357
1358                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1359                 private extern String InternalToLower (CultureInfo culture);
1360
1361                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1362                 private extern String InternalToUpper (CultureInfo culture);
1363
1364                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1365                 internal extern static String InternalAllocateStr (int length);
1366
1367                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1368                 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1369
1370                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1371                 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1372
1373                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1374                 private extern static string InternalIntern (string str);
1375
1376                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1377                 private extern static string InternalIsInterned (string str);
1378         }
1379 }