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