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