6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
10 // (C) 2001 Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004 Novell (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
42 public sealed class String : IConvertible, ICloneable, IEnumerable,
44 IComparable, IComparable<String>
49 [NonSerialized] private int length;
50 [NonSerialized] private char start_char;
52 private const int COMPARE_CASE = 0;
53 private const int COMPARE_INCASE = 1;
54 private const int COMPARE_ORDINAL = 2;
56 public static readonly String Empty = "";
58 public static unsafe bool Equals (string a, string b)
60 if ((a as object) == (b as object))
63 if (a == null || b == null)
74 fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
75 // it must be one char, because 0 len is done above
80 int * sint1 = (int *) s1, sint2 = (int *) s2;
83 if (*sint1++ != *sint2++)
92 return *(char *) sint1 == *(char *) sint2;
96 public static bool operator == (String a, String b)
101 public static bool operator != (String a, String b)
103 return !Equals (a, b);
106 public override bool Equals (Object obj)
108 return Equals (this, obj as String);
111 public bool Equals (String value)
113 return Equals (this, value);
116 [IndexerName ("Chars")]
117 public extern char this [int index] {
118 [MethodImplAttribute (MethodImplOptions.InternalCall)]
122 public Object Clone ()
127 public TypeCode GetTypeCode ()
129 return TypeCode.String;
132 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
134 // LAMESPEC: should I null-terminate?
135 if (destination == null)
136 throw new ArgumentNullException ("destination");
138 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
139 throw new ArgumentOutOfRangeException ();
141 // re-ordered to avoid possible integer overflow
142 if (sourceIndex > Length - count)
143 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
144 // re-ordered to avoid possible integer overflow
145 if (destinationIndex > destination.Length - count)
146 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
148 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
151 public char[] ToCharArray ()
153 return ToCharArray (0, length);
156 public char[] ToCharArray (int startIndex, int length)
159 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
161 throw new ArgumentOutOfRangeException ("length", "< 0");
162 // re-ordered to avoid possible integer overflow
163 if (startIndex > this.length - length)
164 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
166 char[] tmp = new char [length];
168 InternalCopyTo (startIndex, tmp, 0, length);
173 public String [] Split (params char [] separator)
175 return Split (separator, Int32.MaxValue);
178 public String[] Split (char[] separator, int count)
180 if (separator == null || separator.Length == 0)
181 separator = WhiteChars;
184 throw new ArgumentOutOfRangeException ("count");
187 return new String[0];
190 return new String[1] { ToString() };
192 return InternalSplit (separator, count);
195 public unsafe String Substring (int startIndex)
197 if (startIndex < 0 || startIndex > this.length)
198 throw new ArgumentOutOfRangeException ("startIndex");
200 int newlen = this.length - startIndex;
201 string tmp = InternalAllocateStr (newlen);
203 fixed (char *dest = tmp, src = this) {
204 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
210 public unsafe String Substring (int startIndex, int length)
213 throw new ArgumentOutOfRangeException ("length", "< 0");
215 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
216 // re-ordered to avoid possible integer overflow
217 if (startIndex > this.length - length)
218 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
223 string tmp = InternalAllocateStr (length);
224 fixed (char *dest = tmp, src = this) {
225 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
231 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
232 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
233 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
234 (char) 0x3000, (char) 0xFEFF };
236 public String Trim (params char[] trimChars)
238 if (trimChars == null || trimChars.Length == 0)
239 trimChars = WhiteChars;
241 return InternalTrim (trimChars, 0);
244 public String TrimStart (params char[] trimChars)
246 if (trimChars == null || trimChars.Length == 0)
247 trimChars = WhiteChars;
249 return InternalTrim (trimChars, 1);
252 public String TrimEnd (params char[] trimChars)
254 if (trimChars == null || trimChars.Length == 0)
255 trimChars = WhiteChars;
257 return InternalTrim (trimChars, 2);
260 public static int Compare (String strA, String strB)
262 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
265 public static int Compare (String strA, String strB, bool ignoreCase)
267 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
270 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
273 throw new ArgumentNullException ("culture");
282 else if (strB == null) {
286 CompareOptions compopts;
289 compopts = CompareOptions.IgnoreCase;
291 compopts = CompareOptions.None;
293 return culture.CompareInfo.Compare (strA, strB, compopts);
296 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
298 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
301 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
303 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
306 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
309 throw new ArgumentNullException ("culture");
311 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
312 throw new ArgumentOutOfRangeException ();
324 else if (strB == null) {
328 CompareOptions compopts;
331 compopts = CompareOptions.IgnoreCase;
333 compopts = CompareOptions.None;
335 /* Need to cap the requested length to the
336 * length of the string, because
337 * CompareInfo.Compare will insist that length
338 * <= (string.Length - offset)
343 if (length > (strA.Length - indexA)) {
344 len1 = strA.Length - indexA;
347 if (length > (strB.Length - indexB)) {
348 len2 = strB.Length - indexB;
351 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
354 public int CompareTo (Object value)
359 if (!(value is String))
360 throw new ArgumentException ();
362 return String.Compare (this, (String) value, false);
365 public int CompareTo (String strB)
370 return Compare (this, strB, false);
373 public static int CompareOrdinal (String strA, String strB)
381 else if (strB == null) {
385 /* Invariant, because that is cheaper to
386 * instantiate (and chances are it already has
389 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
392 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
394 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
395 throw new ArgumentOutOfRangeException ();
403 else if (strB == null) {
407 /* Need to cap the requested length to the
408 * length of the string, because
409 * CompareInfo.Compare will insist that length
410 * <= (string.Length - offset)
415 if (length > (strA.Length - indexA)) {
416 len1 = strA.Length - indexA;
419 if (length > (strB.Length - indexB)) {
420 len2 = strB.Length - indexB;
423 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
426 public bool EndsWith (String value)
429 throw new ArgumentNullException ("value");
431 if (value.Length == 0)
434 if (value.length > this.length)
437 return (0 == Compare (this, length - value.length, value, 0, value.length));
440 public int IndexOfAny (char [] anyOf)
443 throw new ArgumentNullException ("anyOf");
445 return InternalIndexOfAny (anyOf, 0, this.length);
448 public int IndexOfAny (char [] anyOf, int startIndex)
451 throw new ArgumentNullException ("anyOf");
452 if (startIndex < 0 || startIndex >= this.length)
453 throw new ArgumentOutOfRangeException ("sourceIndex");
455 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
458 public int IndexOfAny (char [] anyOf, int startIndex, int count)
461 throw new ArgumentNullException ("anyOf");
463 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
465 throw new ArgumentOutOfRangeException ("count", "< 0");
466 // re-ordered to avoid possible integer overflow
467 if (startIndex > this.length - count)
468 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
470 return InternalIndexOfAny (anyOf, startIndex, count);
473 public int IndexOf (char value)
475 return IndexOf (value, 0, this.length);
478 public int IndexOf (String value)
480 return IndexOf (value, 0, this.length);
483 public int IndexOf (char value, int startIndex)
485 return IndexOf (value, startIndex, this.length - startIndex);
488 public int IndexOf (String value, int startIndex)
490 return IndexOf (value, startIndex, this.length - startIndex);
493 /* This method is culture-insensitive */
494 public int IndexOf (char value, int startIndex, int count)
497 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
499 throw new ArgumentOutOfRangeException ("count", "< 0");
500 // re-ordered to avoid possible integer overflow
501 if (startIndex > this.length - count)
502 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
504 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
507 for (int pos = startIndex; pos < startIndex + count; pos++) {
508 if (this[pos] == value)
514 /* But this one is culture-sensitive */
515 public int IndexOf (String value, int startIndex, int count)
518 throw new ArgumentNullException ("value");
520 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
522 throw new ArgumentOutOfRangeException ("count", "< 0");
523 // re-ordered to avoid possible integer overflow
524 if (startIndex > this.length - count)
525 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
527 if (value.length == 0)
530 if (startIndex == 0 && this.length == 0)
536 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
539 public int LastIndexOfAny (char [] anyOf)
542 throw new ArgumentNullException ("anyOf");
544 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
547 public int LastIndexOfAny (char [] anyOf, int startIndex)
550 throw new ArgumentNullException ("anyOf");
552 if (startIndex < 0 || startIndex > this.length)
553 throw new ArgumentOutOfRangeException ();
555 if (this.length == 0)
558 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
561 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
564 throw new ArgumentNullException ("anyOf");
566 if ((startIndex < 0) || (startIndex > this.Length))
567 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
568 if ((count < 0) || (count > this.Length))
569 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
570 if (startIndex - count + 1 < 0)
571 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
573 if (this.length == 0)
576 return InternalLastIndexOfAny (anyOf, startIndex, count);
579 public int LastIndexOf (char value)
581 if (this.length == 0)
584 return LastIndexOf (value, this.length - 1, this.length);
587 public int LastIndexOf (String value)
589 if (this.length == 0)
590 /* This overload does additional checking */
591 return LastIndexOf (value, 0, 0);
593 return LastIndexOf (value, this.length - 1, this.length);
596 public int LastIndexOf (char value, int startIndex)
598 return LastIndexOf (value, startIndex, startIndex + 1);
601 public int LastIndexOf (String value, int startIndex)
604 throw new ArgumentNullException ("value");
605 int max = startIndex;
606 if (max < this.Length)
608 return LastIndexOf (value, startIndex, max);
611 /* This method is culture-insensitive */
612 public int LastIndexOf (char value, int startIndex, int count)
614 if (startIndex == 0 && this.length == 0)
617 // >= for char (> for string)
618 if ((startIndex < 0) || (startIndex >= this.Length))
619 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
620 if ((count < 0) || (count > this.Length))
621 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
622 if (startIndex - count + 1 < 0)
623 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
625 for(int pos = startIndex; pos > startIndex - count; pos--) {
626 if (this [pos] == value)
632 /* But this one is culture-sensitive */
633 public int LastIndexOf (String value, int startIndex, int count)
636 throw new ArgumentNullException ("value");
637 // -1 > startIndex > for string (0 > startIndex >= for char)
638 if ((startIndex < -1) || (startIndex > this.Length))
639 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
640 if ((count < 0) || (count > this.Length))
641 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
642 if (startIndex - count + 1 < 0)
643 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
645 if (value.Length == 0)
648 if (startIndex == 0 && this.length == 0)
651 // This check is needed to match undocumented MS behaviour
652 if (this.length == 0 && value.length > 0)
655 if (value.length > startIndex)
661 if (startIndex == this.Length)
663 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
667 public bool Contains (String value)
669 return IndexOf (value) != -1;
672 public static bool IsNullOrEmpty (String value)
674 return (value == null) || (value.Length == 0);
677 public string Remove (int startIndex)
680 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
681 if (startIndex >= this.length)
682 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
684 return Remove (startIndex, this.length - startIndex);
688 public String PadLeft (int totalWidth)
690 return PadLeft (totalWidth, ' ');
693 public String PadLeft (int totalWidth, char paddingChar)
696 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
698 if (totalWidth < this.length)
699 return String.Copy (this);
701 return InternalPad (totalWidth, paddingChar, false);
704 public String PadRight (int totalWidth)
706 return PadRight (totalWidth, ' ');
709 public String PadRight (int totalWidth, char paddingChar)
712 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
714 if (totalWidth < this.length)
715 return String.Copy (this);
717 return InternalPad (totalWidth, paddingChar, true);
720 public bool StartsWith (String value)
723 throw new ArgumentNullException ("value");
725 if (value.Length == 0)
728 if (this.length < value.length)
731 return (0 == Compare (this, 0, value, 0 , value.length));
734 /* This method is culture insensitive */
735 public String Replace (char oldChar, char newChar)
737 return InternalReplace (oldChar, newChar);
740 /* This method is culture sensitive */
741 public String Replace (String oldValue, String newValue)
743 if (oldValue == null)
744 throw new ArgumentNullException ("oldValue");
746 if (oldValue.Length == 0)
747 throw new ArgumentException ("oldValue is the empty string.");
749 if (this.Length == 0)
752 if (newValue == null)
753 newValue = String.Empty;
755 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
758 public unsafe String Remove (int startIndex, int count)
761 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
763 throw new ArgumentOutOfRangeException ("count", "< 0");
764 // re-ordered to avoid possible integer overflow
765 if (startIndex > this.length - count)
766 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
768 String tmp = InternalAllocateStr (this.length - count);
770 fixed (char *dest = tmp, src = this) {
772 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
773 int skip = startIndex + count;
775 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
780 public String ToLower ()
782 // CurrentCulture can never be invariant or null
783 return InternalToLower (CultureInfo.CurrentCulture);
786 public String ToLower (CultureInfo culture)
789 throw new ArgumentNullException ("culture");
791 if (culture.LCID == 0x007F) { // Invariant
792 return ToLowerInvariant ();
794 return InternalToLower (culture);
797 internal unsafe String ToLowerInvariant ()
799 string tmp = InternalAllocateStr (length);
800 fixed (char* source = &start_char, dest = tmp) {
802 char* destPtr = (char*)dest;
803 char* sourcePtr = (char*)source;
805 for (int n = 0; n < length; n++) {
806 *destPtr = Char.ToLowerInvariant (*sourcePtr);
814 public String ToUpper ()
816 // CurrentCulture can never be invariant or null
817 return InternalToUpper (CultureInfo.CurrentCulture);
820 public String ToUpper (CultureInfo culture)
823 throw new ArgumentNullException ("culture");
825 if (culture.LCID == 0x007F) { // Invariant
826 return ToUpperInvariant ();
828 return InternalToUpper (culture);
831 internal unsafe String ToUpperInvariant ()
833 string tmp = InternalAllocateStr (length);
834 fixed (char* source = &start_char, dest = tmp) {
836 char* destPtr = (char*)dest;
837 char* sourcePtr = (char*)source;
839 for (int n = 0; n < length; n++) {
840 *destPtr = Char.ToUpperInvariant (*sourcePtr);
848 public override String ToString ()
853 public String ToString (IFormatProvider provider)
858 public String Trim ()
863 public static String Format (String format, Object arg0)
865 return Format (null, format, new Object[] {arg0});
868 public static String Format (String format, Object arg0, Object arg1)
870 return Format (null, format, new Object[] {arg0, arg1});
873 public static String Format (String format, Object arg0, Object arg1, Object arg2)
875 return Format (null, format, new Object[] {arg0, arg1, arg2});
878 public static string Format (string format, params object[] args)
880 return Format (null, format, args);
883 public static string Format (IFormatProvider provider, string format, params object[] args)
885 StringBuilder b = new StringBuilder ();
886 FormatHelper (b, provider, format, args);
887 return b.ToString ();
890 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
892 if (format == null || args == null)
893 throw new ArgumentNullException ();
897 while (ptr < format.length) {
898 char c = format[ptr ++];
901 result.Append (format, start, ptr - start - 1);
903 // check for escaped open bracket
905 if (format[ptr] == '{') {
916 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
917 if (n >= args.Length)
918 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
922 object arg = args[n];
927 else if (arg is IFormattable)
928 str = ((IFormattable)arg).ToString (arg_format, provider);
930 str = arg.ToString ();
932 // pad formatted string and append to result
934 if (width > str.length) {
935 string pad = new String (' ', width - str.length);
951 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
952 result.Append (format, start, ptr - start - 1);
956 throw new FormatException ("Input string was not in a correct format.");
960 if (start < format.length)
961 result.Append (format.Substring (start));
964 public unsafe static String Copy (String str)
967 throw new ArgumentNullException ("str");
969 int length = str.length;
971 String tmp = InternalAllocateStr (length);
973 fixed (char *dest = tmp, src = str) {
974 memcpy ((byte*)dest, (byte*)src, length * 2);
980 public static String Concat (Object obj)
985 return obj.ToString ();
988 public unsafe static String Concat (Object obj1, Object obj2)
992 s1 = (obj1 != null) ? obj1.ToString () : null;
993 s2 = (obj2 != null) ? obj2.ToString () : null;
1000 } else if (s2 == null)
1003 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1004 if (s1.Length != 0) {
1005 fixed (char *dest = tmp, src = s1) {
1006 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1009 if (s2.Length != 0) {
1010 fixed (char *dest = tmp, src = s2) {
1011 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1018 public static String Concat (Object obj1, Object obj2, Object obj3)
1024 s1 = obj1.ToString ();
1029 s2 = obj2.ToString ();
1034 s3 = obj3.ToString ();
1036 return Concat (s1, s2, s3);
1039 #if ! BOOTSTRAP_WITH_OLDLIB
1040 [CLSCompliant(false)]
1041 public static String Concat (Object obj1, Object obj2, Object obj3,
1042 Object obj4, __arglist)
1044 string s1, s2, s3, s4;
1049 s1 = obj1.ToString ();
1054 s2 = obj2.ToString ();
1059 s3 = obj3.ToString ();
1061 ArgIterator iter = new ArgIterator (__arglist);
1062 int argCount = iter.GetRemainingCount();
1064 StringBuilder sb = new StringBuilder ();
1066 sb.Append (obj4.ToString ());
1068 for (int i = 0; i < argCount; i++) {
1069 TypedReference typedRef = iter.GetNextArg ();
1070 sb.Append (TypedReference.ToObject (typedRef));
1073 s4 = sb.ToString ();
1075 return Concat (s1, s2, s3, s4);
1079 public unsafe static String Concat (String s1, String s2)
1083 return String.Empty;
1090 String tmp = InternalAllocateStr (s1.length + s2.length);
1092 if (s1.Length != 0) {
1093 fixed (char *dest = tmp, src = s1) {
1094 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1097 if (s2.Length != 0) {
1098 fixed (char *dest = tmp, src = s2) {
1099 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1106 public unsafe static String Concat (String s1, String s2, String s3)
1111 return String.Empty;
1130 //return InternalConcat (s1, s2, s3);
1131 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1133 if (s1.Length != 0) {
1134 fixed (char *dest = tmp, src = s1) {
1135 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1138 if (s2.Length != 0) {
1139 fixed (char *dest = tmp, src = s2) {
1140 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1143 if (s3.Length != 0) {
1144 fixed (char *dest = tmp, src = s3) {
1145 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1152 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1154 if (s1 == null && s2 == null && s3 == null && s4 == null)
1155 return String.Empty;
1166 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1168 if (s1.Length != 0) {
1169 fixed (char *dest = tmp, src = s1) {
1170 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1173 if (s2.Length != 0) {
1174 fixed (char *dest = tmp, src = s2) {
1175 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1178 if (s3.Length != 0) {
1179 fixed (char *dest = tmp, src = s3) {
1180 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1183 if (s4.Length != 0) {
1184 fixed (char *dest = tmp, src = s4) {
1185 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1192 public static String Concat (params Object[] args)
1195 throw new ArgumentNullException ("args");
1197 int i = args.Length;
1199 return String.Empty;
1201 string [] strings = new string [i];
1204 foreach (object arg in args) {
1206 strings[i] = String.Empty;
1208 strings[i] = arg.ToString ();
1209 len += strings[i].length;
1215 return String.Empty;
1217 return InternalJoin (String.Empty, strings, 0, strings.Length);
1220 public static String Concat (params String[] values)
1223 throw new ArgumentNullException ("values");
1225 return InternalJoin (String.Empty, values, 0, values.Length);
1228 public unsafe String Insert (int startIndex, String value)
1231 throw new ArgumentNullException ("value");
1233 if (startIndex < 0 || startIndex > this.length)
1234 throw new ArgumentOutOfRangeException ();
1236 if (value.Length == 0)
1238 if (this.Length == 0)
1240 String tmp = InternalAllocateStr (this.length + value.length);
1242 fixed (char *dest = tmp, src = this, val = value) {
1244 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1246 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1247 dst += value.length;
1248 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1254 public static string Intern (string str)
1257 throw new ArgumentNullException ("str");
1259 return InternalIntern (str);
1262 public static string IsInterned (string str)
1265 throw new ArgumentNullException ("str");
1267 return InternalIsInterned (str);
1270 public static string Join (string separator, string [] value)
1273 throw new ArgumentNullException ("value");
1275 return Join (separator, value, 0, value.Length);
1278 public static string Join (string separator, string[] value, int startIndex, int count)
1281 throw new ArgumentNullException ("value");
1283 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1285 throw new ArgumentOutOfRangeException ("count", "< 0");
1286 // re-ordered to avoid possible integer overflow
1287 if (startIndex > value.Length - count)
1288 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1290 if (startIndex == value.Length)
1291 return String.Empty;
1292 if (separator == null)
1293 separator = String.Empty;
1295 return InternalJoin (separator, value, startIndex, count);
1298 bool IConvertible.ToBoolean (IFormatProvider provider)
1300 return Convert.ToBoolean (this, provider);
1303 byte IConvertible.ToByte (IFormatProvider provider)
1305 return Convert.ToByte (this, provider);
1308 char IConvertible.ToChar (IFormatProvider provider)
1310 return Convert.ToChar (this, provider);
1313 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1315 return Convert.ToDateTime (this, provider);
1318 decimal IConvertible.ToDecimal (IFormatProvider provider)
1320 return Convert.ToDecimal (this, provider);
1323 double IConvertible.ToDouble (IFormatProvider provider)
1325 return Convert.ToDouble (this, provider);
1328 short IConvertible.ToInt16 (IFormatProvider provider)
1330 return Convert.ToInt16 (this, provider);
1333 int IConvertible.ToInt32 (IFormatProvider provider)
1335 return Convert.ToInt32 (this, provider);
1338 long IConvertible.ToInt64 (IFormatProvider provider)
1340 return Convert.ToInt64 (this, provider);
1343 sbyte IConvertible.ToSByte (IFormatProvider provider)
1345 return Convert.ToSByte (this, provider);
1348 float IConvertible.ToSingle (IFormatProvider provider)
1350 return Convert.ToSingle (this, provider);
1353 string IConvertible.ToString (IFormatProvider format)
1358 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1360 return Convert.ToType (this, conversionType, provider);
1363 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1365 return Convert.ToUInt16 (this, provider);
1368 uint IConvertible.ToUInt32 (IFormatProvider provider)
1370 return Convert.ToUInt32 (this, provider);
1373 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1375 return Convert.ToUInt64 (this, provider);
1378 TypeCode IConvertible.GetTypeCode ()
1380 return TypeCode.String;
1389 public CharEnumerator GetEnumerator ()
1391 return new CharEnumerator (this);
1394 IEnumerator IEnumerable.GetEnumerator ()
1396 return new CharEnumerator (this);
1399 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1400 out bool left_align, out string format)
1402 // parses format specifier of form:
1408 // N = argument number (non-negative integer)
1410 n = ParseDecimal (str, ref ptr);
1412 throw new FormatException ("Input string was not in a correct format.");
1414 // M = width (non-negative integer)
1416 if (str[ptr] == ',') {
1417 // White space between ',' and number or sign.
1419 while (Char.IsWhiteSpace (str [ptr]))
1422 format = str.Substring (start, ptr - start);
1424 left_align = (str [ptr] == '-');
1428 width = ParseDecimal (str, ref ptr);
1430 throw new FormatException ("Input string was not in a correct format.");
1438 // F = argument format (string)
1440 if (str[ptr] == ':') {
1442 while (str[ptr] != '}')
1445 format += str.Substring (start, ptr - start);
1450 if (str[ptr ++] != '}')
1451 throw new FormatException ("Input string was not in a correct format.");
1453 catch (IndexOutOfRangeException) {
1454 throw new FormatException ("Input string was not in a correct format.");
1458 private static int ParseDecimal (string str, ref int ptr)
1464 if (c < '0' || '9' < c)
1467 n = n * 10 + c - '0';
1478 internal unsafe void InternalSetChar (int idx, char val)
1480 if ((uint) idx >= (uint) Length)
1481 throw new ArgumentOutOfRangeException ("idx");
1483 fixed (char * pStr = &start_char)
1489 internal unsafe void InternalSetLength (int newLength)
1491 if (newLength > length)
1492 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1496 // zero terminate, we can pass string objects directly via pinvoke
1497 fixed (char * pStr = &start_char) {
1498 pStr [length] = '\0';
1502 public unsafe override int GetHashCode ()
1504 fixed (char * c = this) {
1506 char * end = cc + length - 1;
1508 for (;cc < end; cc += 2) {
1509 h = (h << 5) - h + *cc;
1510 h = (h << 5) - h + cc [1];
1514 h = (h << 5) - h + *cc;
1519 /* helpers used by the runtime as well as above or eslewhere in corlib */
1520 internal static unsafe void memset (byte *dest, int val, int len)
1531 val = val | (val << 8);
1532 val = val | (val << 16);
1535 int rest = (int)dest & 3;
1543 } while (rest != 0);
1546 ((int*)dest) [0] = val;
1547 ((int*)dest) [1] = val;
1548 ((int*)dest) [2] = val;
1549 ((int*)dest) [3] = val;
1554 ((int*)dest) [0] = val;
1566 internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
1567 /*while (size >= 32) {
1568 // using long is better than int and slower than double
1569 // FIXME: enable this only on correct alignment or on platforms
1570 // that can tolerate unaligned reads/writes of doubles
1571 ((double*)dest) [0] = ((double*)src) [0];
1572 ((double*)dest) [1] = ((double*)src) [1];
1573 ((double*)dest) [2] = ((double*)src) [2];
1574 ((double*)dest) [3] = ((double*)src) [3];
1579 while (size >= 16) {
1580 ((int*)dest) [0] = ((int*)src) [0];
1581 ((int*)dest) [1] = ((int*)src) [1];
1582 ((int*)dest) [2] = ((int*)src) [2];
1583 ((int*)dest) [3] = ((int*)src) [3];
1589 ((int*)dest) [0] = ((int*)src) [0];
1595 ((byte*)dest) [0] = ((byte*)src) [0];
1601 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
1603 ((short*)dest) [0] = ((short*)src) [0];
1604 ((short*)dest) [1] = ((short*)src) [1];
1605 ((short*)dest) [2] = ((short*)src) [2];
1606 ((short*)dest) [3] = ((short*)src) [3];
1612 ((short*)dest) [0] = ((short*)src) [0];
1618 ((byte*)dest) [0] = ((byte*)src) [0];
1620 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
1622 ((byte*)dest) [0] = ((byte*)src) [0];
1623 ((byte*)dest) [1] = ((byte*)src) [1];
1624 ((byte*)dest) [2] = ((byte*)src) [2];
1625 ((byte*)dest) [3] = ((byte*)src) [3];
1626 ((byte*)dest) [4] = ((byte*)src) [4];
1627 ((byte*)dest) [5] = ((byte*)src) [5];
1628 ((byte*)dest) [6] = ((byte*)src) [6];
1629 ((byte*)dest) [7] = ((byte*)src) [7];
1635 ((byte*)dest) [0] = ((byte*)src) [0];
1636 ((byte*)dest) [1] = ((byte*)src) [1];
1642 ((byte*)dest) [0] = ((byte*)src) [0];
1644 static unsafe void memcpy (byte *dest, byte *src, int size) {
1645 // FIXME: if pointers are not aligned, try to align them
1646 // so a faster routine can be used. Handle the case where
1647 // the pointers can't be reduced to have the same alignment
1648 // (just ignore the issue on x86?)
1649 if ((((int)dest | (int)src) & 3) != 0) {
1650 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
1656 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
1657 ((short*)dest) [0] = ((short*)src) [0];
1662 if ((((int)dest | (int)src) & 1) != 0) {
1663 memcpy1 (dest, src, size);
1666 if ((((int)dest | (int)src) & 2) != 0) {
1667 memcpy2 (dest, src, size);
1671 memcpy4 (dest, src, size);
1674 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1675 unsafe public extern String (char *value);
1677 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1678 unsafe public extern String (char *value, int startIndex, int length);
1680 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1681 unsafe public extern String (sbyte *value);
1683 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1684 unsafe public extern String (sbyte *value, int startIndex, int length);
1686 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1687 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1689 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1690 public extern String (char [] val, int startIndex, int length);
1692 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1693 public extern String (char [] val);
1695 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1696 public extern String (char c, int count);
1698 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1699 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1701 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1702 private extern String InternalReplace (char oldChar, char newChar);
1704 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1705 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1707 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1708 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1710 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1711 private extern String[] InternalSplit (char[] separator, int count);
1713 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1714 private extern String InternalTrim (char[] chars, int typ);
1716 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1717 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1719 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1720 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1722 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1723 private extern String InternalPad (int width, char chr, bool right);
1725 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1726 private extern String InternalToLower (CultureInfo culture);
1728 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1729 private extern String InternalToUpper (CultureInfo culture);
1731 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1732 internal extern static String InternalAllocateStr (int length);
1734 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1735 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1737 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1738 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1740 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1741 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1743 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1744 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1746 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1747 private extern static string InternalIntern (string str);
1749 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1750 private extern static string InternalIsInterned (string str);