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