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 return ToLower (CultureInfo.CurrentCulture);
785 public String ToLower (CultureInfo culture)
788 throw new ArgumentNullException ("culture");
790 if (culture.LCID == 0x007F) { // Invariant
791 return ToLowerInvariant ();
793 return culture.TextInfo.ToLower (this);
796 internal unsafe String ToLowerInvariant ()
798 string tmp = InternalAllocateStr (length);
799 fixed (char* source = &start_char, dest = tmp) {
801 char* destPtr = (char*)dest;
802 char* sourcePtr = (char*)source;
804 for (int n = 0; n < length; n++) {
805 *destPtr = Char.ToLowerInvariant (*sourcePtr);
813 public String ToUpper ()
815 return ToUpper (CultureInfo.CurrentCulture);
818 public String ToUpper (CultureInfo culture)
821 throw new ArgumentNullException ("culture");
823 if (culture.LCID == 0x007F) { // Invariant
824 return ToUpperInvariant ();
826 return culture.TextInfo.ToUpper (this);
829 internal unsafe String ToUpperInvariant ()
831 string tmp = InternalAllocateStr (length);
832 fixed (char* source = &start_char, dest = tmp) {
834 char* destPtr = (char*)dest;
835 char* sourcePtr = (char*)source;
837 for (int n = 0; n < length; n++) {
838 *destPtr = Char.ToUpperInvariant (*sourcePtr);
846 public override String ToString ()
851 public String ToString (IFormatProvider provider)
856 public String Trim ()
861 public static String Format (String format, Object arg0)
863 return Format (null, format, new Object[] {arg0});
866 public static String Format (String format, Object arg0, Object arg1)
868 return Format (null, format, new Object[] {arg0, arg1});
871 public static String Format (String format, Object arg0, Object arg1, Object arg2)
873 return Format (null, format, new Object[] {arg0, arg1, arg2});
876 public static string Format (string format, params object[] args)
878 return Format (null, format, args);
881 public static string Format (IFormatProvider provider, string format, params object[] args)
883 StringBuilder b = new StringBuilder ();
884 FormatHelper (b, provider, format, args);
885 return b.ToString ();
888 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
890 if (format == null || args == null)
891 throw new ArgumentNullException ();
895 while (ptr < format.length) {
896 char c = format[ptr ++];
899 result.Append (format, start, ptr - start - 1);
901 // check for escaped open bracket
903 if (format[ptr] == '{') {
914 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
915 if (n >= args.Length)
916 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
920 object arg = args[n];
925 else if (arg is IFormattable)
926 str = ((IFormattable)arg).ToString (arg_format, provider);
928 str = arg.ToString ();
930 // pad formatted string and append to result
932 if (width > str.length) {
933 const char padchar = ' ';
934 int padlen = width - str.length;
938 result.Append (padchar, padlen);
941 result.Append (padchar, padlen);
950 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
951 result.Append (format, start, ptr - start - 1);
955 throw new FormatException ("Input string was not in a correct format.");
959 if (start < format.length)
960 result.Append (format, start, format.Length - start);
963 public unsafe static String Copy (String str)
966 throw new ArgumentNullException ("str");
968 int length = str.length;
970 String tmp = InternalAllocateStr (length);
972 fixed (char *dest = tmp, src = str) {
973 memcpy ((byte*)dest, (byte*)src, length * 2);
979 public static String Concat (Object obj)
984 return obj.ToString ();
987 public unsafe static String Concat (Object obj1, Object obj2)
991 s1 = (obj1 != null) ? obj1.ToString () : null;
992 s2 = (obj2 != null) ? obj2.ToString () : null;
999 } else if (s2 == null)
1002 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1003 if (s1.Length != 0) {
1004 fixed (char *dest = tmp, src = s1) {
1005 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1008 if (s2.Length != 0) {
1009 fixed (char *dest = tmp, src = s2) {
1010 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1017 public static String Concat (Object obj1, Object obj2, Object obj3)
1023 s1 = obj1.ToString ();
1028 s2 = obj2.ToString ();
1033 s3 = obj3.ToString ();
1035 return Concat (s1, s2, s3);
1038 #if ! BOOTSTRAP_WITH_OLDLIB
1039 [CLSCompliant(false)]
1040 public static String Concat (Object obj1, Object obj2, Object obj3,
1041 Object obj4, __arglist)
1043 string s1, s2, s3, s4;
1048 s1 = obj1.ToString ();
1053 s2 = obj2.ToString ();
1058 s3 = obj3.ToString ();
1060 ArgIterator iter = new ArgIterator (__arglist);
1061 int argCount = iter.GetRemainingCount();
1063 StringBuilder sb = new StringBuilder ();
1065 sb.Append (obj4.ToString ());
1067 for (int i = 0; i < argCount; i++) {
1068 TypedReference typedRef = iter.GetNextArg ();
1069 sb.Append (TypedReference.ToObject (typedRef));
1072 s4 = sb.ToString ();
1074 return Concat (s1, s2, s3, s4);
1078 public unsafe static String Concat (String s1, String s2)
1082 return String.Empty;
1089 String tmp = InternalAllocateStr (s1.length + s2.length);
1091 if (s1.Length != 0) {
1092 fixed (char *dest = tmp, src = s1) {
1093 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1096 if (s2.Length != 0) {
1097 fixed (char *dest = tmp, src = s2) {
1098 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1105 public unsafe static String Concat (String s1, String s2, String s3)
1110 return String.Empty;
1129 //return InternalConcat (s1, s2, s3);
1130 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1132 if (s1.Length != 0) {
1133 fixed (char *dest = tmp, src = s1) {
1134 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1137 if (s2.Length != 0) {
1138 fixed (char *dest = tmp, src = s2) {
1139 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1142 if (s3.Length != 0) {
1143 fixed (char *dest = tmp, src = s3) {
1144 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1151 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1153 if (s1 == null && s2 == null && s3 == null && s4 == null)
1154 return String.Empty;
1165 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1167 if (s1.Length != 0) {
1168 fixed (char *dest = tmp, src = s1) {
1169 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1172 if (s2.Length != 0) {
1173 fixed (char *dest = tmp, src = s2) {
1174 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1177 if (s3.Length != 0) {
1178 fixed (char *dest = tmp, src = s3) {
1179 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1182 if (s4.Length != 0) {
1183 fixed (char *dest = tmp, src = s4) {
1184 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1191 public static String Concat (params Object[] args)
1194 throw new ArgumentNullException ("args");
1196 int i = args.Length;
1198 return String.Empty;
1200 string [] strings = new string [i];
1203 foreach (object arg in args) {
1205 strings[i] = String.Empty;
1207 strings[i] = arg.ToString ();
1208 len += strings[i].length;
1214 return String.Empty;
1216 return InternalJoin (String.Empty, strings, 0, strings.Length);
1219 public static String Concat (params String[] values)
1222 throw new ArgumentNullException ("values");
1224 return InternalJoin (String.Empty, values, 0, values.Length);
1227 public unsafe String Insert (int startIndex, String value)
1230 throw new ArgumentNullException ("value");
1232 if (startIndex < 0 || startIndex > this.length)
1233 throw new ArgumentOutOfRangeException ();
1235 if (value.Length == 0)
1237 if (this.Length == 0)
1239 String tmp = InternalAllocateStr (this.length + value.length);
1241 fixed (char *dest = tmp, src = this, val = value) {
1243 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1245 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1246 dst += value.length;
1247 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1253 public static string Intern (string str)
1256 throw new ArgumentNullException ("str");
1258 return InternalIntern (str);
1261 public static string IsInterned (string str)
1264 throw new ArgumentNullException ("str");
1266 return InternalIsInterned (str);
1269 public static string Join (string separator, string [] value)
1272 throw new ArgumentNullException ("value");
1274 return Join (separator, value, 0, value.Length);
1277 public static string Join (string separator, string[] value, int startIndex, int count)
1280 throw new ArgumentNullException ("value");
1282 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1284 throw new ArgumentOutOfRangeException ("count", "< 0");
1285 // re-ordered to avoid possible integer overflow
1286 if (startIndex > value.Length - count)
1287 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1289 if (startIndex == value.Length)
1290 return String.Empty;
1291 if (separator == null)
1292 separator = String.Empty;
1294 return InternalJoin (separator, value, startIndex, count);
1297 bool IConvertible.ToBoolean (IFormatProvider provider)
1299 return Convert.ToBoolean (this, provider);
1302 byte IConvertible.ToByte (IFormatProvider provider)
1304 return Convert.ToByte (this, provider);
1307 char IConvertible.ToChar (IFormatProvider provider)
1309 return Convert.ToChar (this, provider);
1312 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1314 return Convert.ToDateTime (this, provider);
1317 decimal IConvertible.ToDecimal (IFormatProvider provider)
1319 return Convert.ToDecimal (this, provider);
1322 double IConvertible.ToDouble (IFormatProvider provider)
1324 return Convert.ToDouble (this, provider);
1327 short IConvertible.ToInt16 (IFormatProvider provider)
1329 return Convert.ToInt16 (this, provider);
1332 int IConvertible.ToInt32 (IFormatProvider provider)
1334 return Convert.ToInt32 (this, provider);
1337 long IConvertible.ToInt64 (IFormatProvider provider)
1339 return Convert.ToInt64 (this, provider);
1342 sbyte IConvertible.ToSByte (IFormatProvider provider)
1344 return Convert.ToSByte (this, provider);
1347 float IConvertible.ToSingle (IFormatProvider provider)
1349 return Convert.ToSingle (this, provider);
1352 string IConvertible.ToString (IFormatProvider format)
1357 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1359 return Convert.ToType (this, conversionType, provider);
1362 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1364 return Convert.ToUInt16 (this, provider);
1367 uint IConvertible.ToUInt32 (IFormatProvider provider)
1369 return Convert.ToUInt32 (this, provider);
1372 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1374 return Convert.ToUInt64 (this, provider);
1377 TypeCode IConvertible.GetTypeCode ()
1379 return TypeCode.String;
1388 public CharEnumerator GetEnumerator ()
1390 return new CharEnumerator (this);
1393 IEnumerator IEnumerable.GetEnumerator ()
1395 return new CharEnumerator (this);
1398 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1399 out bool left_align, out string format)
1401 // parses format specifier of form:
1407 // N = argument number (non-negative integer)
1409 n = ParseDecimal (str, ref ptr);
1411 throw new FormatException ("Input string was not in a correct format.");
1413 // M = width (non-negative integer)
1415 if (str[ptr] == ',') {
1416 // White space between ',' and number or sign.
1418 while (Char.IsWhiteSpace (str [ptr]))
1421 format = str.Substring (start, ptr - start);
1423 left_align = (str [ptr] == '-');
1427 width = ParseDecimal (str, ref ptr);
1429 throw new FormatException ("Input string was not in a correct format.");
1437 // F = argument format (string)
1439 if (str[ptr] == ':') {
1441 while (str[ptr] != '}')
1444 format += str.Substring (start, ptr - start);
1449 if (str[ptr ++] != '}')
1450 throw new FormatException ("Input string was not in a correct format.");
1452 catch (IndexOutOfRangeException) {
1453 throw new FormatException ("Input string was not in a correct format.");
1457 private static int ParseDecimal (string str, ref int ptr)
1463 if (c < '0' || '9' < c)
1466 n = n * 10 + c - '0';
1477 internal unsafe void InternalSetChar (int idx, char val)
1479 if ((uint) idx >= (uint) Length)
1480 throw new ArgumentOutOfRangeException ("idx");
1482 fixed (char * pStr = &start_char)
1488 internal unsafe void InternalSetLength (int newLength)
1490 if (newLength > length)
1491 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1495 // zero terminate, we can pass string objects directly via pinvoke
1496 fixed (char * pStr = &start_char) {
1497 pStr [length] = '\0';
1501 public unsafe override int GetHashCode ()
1503 fixed (char * c = this) {
1505 char * end = cc + length - 1;
1507 for (;cc < end; cc += 2) {
1508 h = (h << 5) - h + *cc;
1509 h = (h << 5) - h + cc [1];
1513 h = (h << 5) - h + *cc;
1518 /* helpers used by the runtime as well as above or eslewhere in corlib */
1519 internal static unsafe void memset (byte *dest, int val, int len)
1530 val = val | (val << 8);
1531 val = val | (val << 16);
1534 int rest = (int)dest & 3;
1542 } while (rest != 0);
1545 ((int*)dest) [0] = val;
1546 ((int*)dest) [1] = val;
1547 ((int*)dest) [2] = val;
1548 ((int*)dest) [3] = val;
1553 ((int*)dest) [0] = val;
1565 internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
1566 /*while (size >= 32) {
1567 // using long is better than int and slower than double
1568 // FIXME: enable this only on correct alignment or on platforms
1569 // that can tolerate unaligned reads/writes of doubles
1570 ((double*)dest) [0] = ((double*)src) [0];
1571 ((double*)dest) [1] = ((double*)src) [1];
1572 ((double*)dest) [2] = ((double*)src) [2];
1573 ((double*)dest) [3] = ((double*)src) [3];
1578 while (size >= 16) {
1579 ((int*)dest) [0] = ((int*)src) [0];
1580 ((int*)dest) [1] = ((int*)src) [1];
1581 ((int*)dest) [2] = ((int*)src) [2];
1582 ((int*)dest) [3] = ((int*)src) [3];
1588 ((int*)dest) [0] = ((int*)src) [0];
1594 ((byte*)dest) [0] = ((byte*)src) [0];
1600 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
1602 ((short*)dest) [0] = ((short*)src) [0];
1603 ((short*)dest) [1] = ((short*)src) [1];
1604 ((short*)dest) [2] = ((short*)src) [2];
1605 ((short*)dest) [3] = ((short*)src) [3];
1611 ((short*)dest) [0] = ((short*)src) [0];
1617 ((byte*)dest) [0] = ((byte*)src) [0];
1619 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
1621 ((byte*)dest) [0] = ((byte*)src) [0];
1622 ((byte*)dest) [1] = ((byte*)src) [1];
1623 ((byte*)dest) [2] = ((byte*)src) [2];
1624 ((byte*)dest) [3] = ((byte*)src) [3];
1625 ((byte*)dest) [4] = ((byte*)src) [4];
1626 ((byte*)dest) [5] = ((byte*)src) [5];
1627 ((byte*)dest) [6] = ((byte*)src) [6];
1628 ((byte*)dest) [7] = ((byte*)src) [7];
1634 ((byte*)dest) [0] = ((byte*)src) [0];
1635 ((byte*)dest) [1] = ((byte*)src) [1];
1641 ((byte*)dest) [0] = ((byte*)src) [0];
1643 static unsafe void memcpy (byte *dest, byte *src, int size) {
1644 // FIXME: if pointers are not aligned, try to align them
1645 // so a faster routine can be used. Handle the case where
1646 // the pointers can't be reduced to have the same alignment
1647 // (just ignore the issue on x86?)
1648 if ((((int)dest | (int)src) & 3) != 0) {
1649 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
1655 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
1656 ((short*)dest) [0] = ((short*)src) [0];
1661 if ((((int)dest | (int)src) & 1) != 0) {
1662 memcpy1 (dest, src, size);
1665 if ((((int)dest | (int)src) & 2) != 0) {
1666 memcpy2 (dest, src, size);
1670 memcpy4 (dest, src, size);
1673 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1674 unsafe public extern String (char *value);
1676 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1677 unsafe public extern String (char *value, int startIndex, int length);
1679 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1680 unsafe public extern String (sbyte *value);
1682 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1683 unsafe public extern String (sbyte *value, int startIndex, int length);
1685 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1686 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1688 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1689 public extern String (char [] val, int startIndex, int length);
1691 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1692 public extern String (char [] val);
1694 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1695 public extern String (char c, int count);
1697 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1698 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1700 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1701 private extern String InternalReplace (char oldChar, char newChar);
1703 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1704 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1706 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1707 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1709 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1710 private extern String[] InternalSplit (char[] separator, int count);
1712 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1713 private extern String InternalTrim (char[] chars, int typ);
1715 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1716 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1718 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1719 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1721 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1722 private extern String InternalPad (int width, char chr, bool right);
1724 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1725 internal extern static String InternalAllocateStr (int length);
1727 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1728 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1730 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1731 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1733 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1734 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1736 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1737 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1739 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1740 private extern static string InternalIntern (string str);
1742 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1743 private extern static string InternalIsInterned (string str);