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