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-2005 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.
34 using System.Collections;
35 using System.Globalization;
36 using System.Runtime.CompilerServices;
39 using System.Runtime.ConstrainedExecution;
40 using System.Runtime.InteropServices;
48 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>
50 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
53 [NonSerialized] private int length;
54 [NonSerialized] private char start_char;
56 private const int COMPARE_CASE = 0;
57 private const int COMPARE_INCASE = 1;
58 private const int COMPARE_ORDINAL = 2;
60 public static readonly String Empty = "";
62 public static unsafe bool Equals (string a, string b)
64 if ((a as object) == (b as object))
67 if (a == null || b == null)
78 fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
79 // it must be one char, because 0 len is done above
84 int * sint1 = (int *) s1, sint2 = (int *) s2;
87 if (*sint1++ != *sint2++)
96 return *(char *) sint1 == *(char *) sint2;
100 public static bool operator == (String a, String b)
102 return Equals (a, b);
105 public static bool operator != (String a, String b)
107 return !Equals (a, b);
111 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
113 public override bool Equals (Object obj)
115 return Equals (this, obj as String);
119 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
121 public bool Equals (String value)
123 return Equals (this, value);
126 [IndexerName ("Chars")]
127 public extern char this [int index] {
128 [MethodImplAttribute (MethodImplOptions.InternalCall)]
132 public Object Clone ()
137 public TypeCode GetTypeCode ()
139 return TypeCode.String;
142 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
144 // LAMESPEC: should I null-terminate?
145 if (destination == null)
146 throw new ArgumentNullException ("destination");
148 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
149 throw new ArgumentOutOfRangeException ();
151 // re-ordered to avoid possible integer overflow
152 if (sourceIndex > Length - count)
153 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
154 // re-ordered to avoid possible integer overflow
155 if (destinationIndex > destination.Length - count)
156 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
158 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
161 public char[] ToCharArray ()
163 return ToCharArray (0, length);
166 public char[] ToCharArray (int startIndex, int length)
169 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
171 throw new ArgumentOutOfRangeException ("length", "< 0");
172 // re-ordered to avoid possible integer overflow
173 if (startIndex > this.length - length)
174 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
176 char[] tmp = new char [length];
178 InternalCopyTo (startIndex, tmp, 0, length);
183 public String [] Split (params char [] separator)
185 return Split (separator, Int32.MaxValue);
188 public String[] Split (char[] separator, int count)
190 if (separator == null || separator.Length == 0)
191 separator = WhiteChars;
194 throw new ArgumentOutOfRangeException ("count");
197 return new String[0];
200 return new String[1] { ToString() };
202 return InternalSplit (separator, count);
205 public unsafe String Substring (int startIndex)
207 if (startIndex < 0 || startIndex > this.length)
208 throw new ArgumentOutOfRangeException ("startIndex");
210 int newlen = this.length - startIndex;
211 string tmp = InternalAllocateStr (newlen);
213 fixed (char *dest = tmp, src = this) {
214 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
220 public unsafe String Substring (int startIndex, int length)
223 throw new ArgumentOutOfRangeException ("length", "< 0");
225 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
226 // re-ordered to avoid possible integer overflow
227 if (startIndex > this.length - length)
228 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
233 string tmp = InternalAllocateStr (length);
234 fixed (char *dest = tmp, src = this) {
235 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
241 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
242 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
243 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
244 (char) 0x3000, (char) 0xFEFF };
246 public String Trim ()
248 return InternalTrim (WhiteChars, 0);
251 public String Trim (params char[] trimChars)
253 if (trimChars == null || trimChars.Length == 0)
254 trimChars = WhiteChars;
256 return InternalTrim (trimChars, 0);
259 public String TrimStart (params char[] trimChars)
261 if (trimChars == null || trimChars.Length == 0)
262 trimChars = WhiteChars;
264 return InternalTrim (trimChars, 1);
267 public String TrimEnd (params char[] trimChars)
269 if (trimChars == null || trimChars.Length == 0)
270 trimChars = WhiteChars;
272 return InternalTrim (trimChars, 2);
275 public static int Compare (String strA, String strB)
277 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
280 public static int Compare (String strA, String strB, bool ignoreCase)
282 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
285 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
288 throw new ArgumentNullException ("culture");
297 else if (strB == null) {
301 CompareOptions compopts;
304 compopts = CompareOptions.IgnoreCase;
306 compopts = CompareOptions.None;
308 return culture.CompareInfo.Compare (strA, strB, compopts);
311 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
313 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
316 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
318 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
321 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
324 throw new ArgumentNullException ("culture");
326 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
327 throw new ArgumentOutOfRangeException ();
339 else if (strB == null) {
343 CompareOptions compopts;
346 compopts = CompareOptions.IgnoreCase;
348 compopts = CompareOptions.None;
350 /* Need to cap the requested length to the
351 * length of the string, because
352 * CompareInfo.Compare will insist that length
353 * <= (string.Length - offset)
358 if (length > (strA.Length - indexA)) {
359 len1 = strA.Length - indexA;
362 if (length > (strB.Length - indexB)) {
363 len2 = strB.Length - indexB;
366 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
369 public int CompareTo (Object value)
374 if (!(value is String))
375 throw new ArgumentException ();
377 return String.Compare (this, (String) value, false);
380 public int CompareTo (String strB)
385 return Compare (this, strB, false);
388 public static int CompareOrdinal (String strA, String strB)
396 else if (strB == null) {
400 /* Invariant, because that is cheaper to
401 * instantiate (and chances are it already has
404 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
407 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
409 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
410 throw new ArgumentOutOfRangeException ();
418 else if (strB == null) {
422 /* Need to cap the requested length to the
423 * length of the string, because
424 * CompareInfo.Compare will insist that length
425 * <= (string.Length - offset)
430 if (length > (strA.Length - indexA)) {
431 len1 = strA.Length - indexA;
434 if (length > (strB.Length - indexB)) {
435 len2 = strB.Length - indexB;
438 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
441 public bool EndsWith (String value)
444 throw new ArgumentNullException ("value");
446 if (value.Length == 0)
449 if (value.length > this.length)
452 return (0 == Compare (this, length - value.length, value, 0, value.length));
455 public int IndexOfAny (char [] anyOf)
458 throw new ArgumentNullException ("anyOf");
460 return InternalIndexOfAny (anyOf, 0, this.length);
463 public int IndexOfAny (char [] anyOf, int startIndex)
466 throw new ArgumentNullException ("anyOf");
467 if (startIndex < 0 || startIndex > this.length)
468 throw new ArgumentOutOfRangeException ("startIndex");
470 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
473 public int IndexOfAny (char [] anyOf, int startIndex, int count)
476 throw new ArgumentNullException ("anyOf");
478 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
480 throw new ArgumentOutOfRangeException ("count", "< 0");
481 // re-ordered to avoid possible integer overflow
482 if (startIndex > this.length - count)
483 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
485 return InternalIndexOfAny (anyOf, startIndex, count);
488 public int IndexOf (char value)
490 return IndexOf (value, 0, this.length);
493 public int IndexOf (String value)
495 return IndexOf (value, 0, this.length);
498 public int IndexOf (char value, int startIndex)
500 return IndexOf (value, startIndex, this.length - startIndex);
503 public int IndexOf (String value, int startIndex)
505 return IndexOf (value, startIndex, this.length - startIndex);
508 /* This method is culture-insensitive */
509 public int IndexOf (char value, int startIndex, int count)
512 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
514 throw new ArgumentOutOfRangeException ("count", "< 0");
515 // re-ordered to avoid possible integer overflow
516 if (startIndex > this.length - count)
517 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
519 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
522 for (int pos = startIndex; pos < startIndex + count; pos++) {
523 if (this[pos] == value)
529 /* But this one is culture-sensitive */
530 public int IndexOf (String value, int startIndex, int count)
533 throw new ArgumentNullException ("value");
535 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
537 throw new ArgumentOutOfRangeException ("count", "< 0");
538 // re-ordered to avoid possible integer overflow
539 if (startIndex > this.length - count)
540 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
542 if (value.length == 0)
545 if (startIndex == 0 && this.length == 0)
551 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
554 public int LastIndexOfAny (char [] anyOf)
557 throw new ArgumentNullException ("anyOf");
559 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
562 public int LastIndexOfAny (char [] anyOf, int startIndex)
565 throw new ArgumentNullException ("anyOf");
567 if (startIndex < 0 || startIndex > this.length)
568 throw new ArgumentOutOfRangeException ();
570 if (this.length == 0)
573 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
576 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
579 throw new ArgumentNullException ("anyOf");
581 if ((startIndex < 0) || (startIndex > this.Length))
582 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
583 if ((count < 0) || (count > this.Length))
584 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
585 if (startIndex - count + 1 < 0)
586 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
588 if (this.length == 0)
591 return InternalLastIndexOfAny (anyOf, startIndex, count);
594 public int LastIndexOf (char value)
596 if (this.length == 0)
599 return LastIndexOf (value, this.length - 1, this.length);
602 public int LastIndexOf (String value)
604 if (this.length == 0)
605 /* This overload does additional checking */
606 return LastIndexOf (value, 0, 0);
608 return LastIndexOf (value, this.length - 1, this.length);
611 public int LastIndexOf (char value, int startIndex)
613 return LastIndexOf (value, startIndex, startIndex + 1);
616 public int LastIndexOf (String value, int startIndex)
619 throw new ArgumentNullException ("value");
620 int max = startIndex;
621 if (max < this.Length)
623 return LastIndexOf (value, startIndex, max);
626 /* This method is culture-insensitive */
627 public int LastIndexOf (char value, int startIndex, int count)
629 if (startIndex == 0 && this.length == 0)
632 // >= for char (> for string)
633 if ((startIndex < 0) || (startIndex >= this.Length))
634 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
635 if ((count < 0) || (count > this.Length))
636 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
637 if (startIndex - count + 1 < 0)
638 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
640 for(int pos = startIndex; pos > startIndex - count; pos--) {
641 if (this [pos] == value)
647 /* But this one is culture-sensitive */
648 public int LastIndexOf (String value, int startIndex, int count)
651 throw new ArgumentNullException ("value");
652 // -1 > startIndex > for string (0 > startIndex >= for char)
653 if ((startIndex < -1) || (startIndex > this.Length))
654 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
655 if ((count < 0) || (count > this.Length))
656 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
657 if (startIndex - count + 1 < 0)
658 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
660 if (value.Length == 0)
663 if (startIndex == 0 && this.length == 0)
666 // This check is needed to match undocumented MS behaviour
667 if (this.length == 0 && value.length > 0)
670 if (value.length > startIndex)
676 if (startIndex == this.Length)
678 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
682 public bool Contains (String value)
684 return IndexOf (value) != -1;
687 public static bool IsNullOrEmpty (String value)
689 return (value == null) || (value.Length == 0);
692 public string Remove (int startIndex)
695 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
696 if (startIndex >= this.length)
697 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
699 return Remove (startIndex, this.length - startIndex);
703 public String PadLeft (int totalWidth)
705 return PadLeft (totalWidth, ' ');
708 public String PadLeft (int totalWidth, char paddingChar)
711 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
713 if (totalWidth < this.length)
714 return String.Copy (this);
716 return InternalPad (totalWidth, paddingChar, false);
719 public String PadRight (int totalWidth)
721 return PadRight (totalWidth, ' ');
724 public String PadRight (int totalWidth, char paddingChar)
727 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
729 if (totalWidth < this.length)
730 return String.Copy (this);
732 return InternalPad (totalWidth, paddingChar, true);
735 public bool StartsWith (String value)
738 throw new ArgumentNullException ("value");
740 if (value.Length == 0)
743 if (this.length < value.length)
746 return (0 == Compare (this, 0, value, 0 , value.length));
749 /* This method is culture insensitive */
750 public String Replace (char oldChar, char newChar)
752 return InternalReplace (oldChar, newChar);
755 /* This method is culture sensitive */
756 public String Replace (String oldValue, String newValue)
758 if (oldValue == null)
759 throw new ArgumentNullException ("oldValue");
761 if (oldValue.Length == 0)
762 throw new ArgumentException ("oldValue is the empty string.");
764 if (this.Length == 0)
767 if (newValue == null)
768 newValue = String.Empty;
770 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
773 public unsafe String Remove (int startIndex, int count)
776 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
778 throw new ArgumentOutOfRangeException ("count", "< 0");
779 // re-ordered to avoid possible integer overflow
780 if (startIndex > this.length - count)
781 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
783 String tmp = InternalAllocateStr (this.length - count);
785 fixed (char *dest = tmp, src = this) {
787 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
788 int skip = startIndex + count;
790 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
795 public String ToLower ()
797 return ToLower (CultureInfo.CurrentCulture);
800 public String ToLower (CultureInfo culture)
803 throw new ArgumentNullException ("culture");
805 if (culture.LCID == 0x007F) { // Invariant
806 return ToLowerInvariant ();
808 return culture.TextInfo.ToLower (this);
812 public unsafe String ToLowerInvariant ()
814 internal unsafe String ToLowerInvariant ()
817 string tmp = InternalAllocateStr (length);
818 fixed (char* source = &start_char, dest = tmp) {
820 char* destPtr = (char*)dest;
821 char* sourcePtr = (char*)source;
823 for (int n = 0; n < length; n++) {
824 *destPtr = Char.ToLowerInvariant (*sourcePtr);
832 public String ToUpper ()
834 return ToUpper (CultureInfo.CurrentCulture);
837 public String ToUpper (CultureInfo culture)
840 throw new ArgumentNullException ("culture");
842 if (culture.LCID == 0x007F) { // Invariant
843 return ToUpperInvariant ();
845 return culture.TextInfo.ToUpper (this);
849 public unsafe String ToUpperInvariant ()
851 internal unsafe String ToUpperInvariant ()
854 string tmp = InternalAllocateStr (length);
855 fixed (char* source = &start_char, dest = tmp) {
857 char* destPtr = (char*)dest;
858 char* sourcePtr = (char*)source;
860 for (int n = 0; n < length; n++) {
861 *destPtr = Char.ToUpperInvariant (*sourcePtr);
869 public override String ToString ()
874 public String ToString (IFormatProvider provider)
879 public static String Format (String format, Object arg0)
881 return Format (null, format, new Object[] {arg0});
884 public static String Format (String format, Object arg0, Object arg1)
886 return Format (null, format, new Object[] {arg0, arg1});
889 public static String Format (String format, Object arg0, Object arg1, Object arg2)
891 return Format (null, format, new Object[] {arg0, arg1, arg2});
894 public static string Format (string format, params object[] args)
896 return Format (null, format, args);
899 public static string Format (IFormatProvider provider, string format, params object[] args)
901 StringBuilder b = new StringBuilder ();
902 FormatHelper (b, provider, format, args);
903 return b.ToString ();
906 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
908 if (format == null || args == null)
909 throw new ArgumentNullException ();
913 while (ptr < format.length) {
914 char c = format[ptr ++];
917 result.Append (format, start, ptr - start - 1);
919 // check for escaped open bracket
921 if (format[ptr] == '{') {
932 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
933 if (n >= args.Length)
934 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
938 object arg = args[n];
943 else if (arg is IFormattable)
944 str = ((IFormattable)arg).ToString (arg_format, provider);
946 str = arg.ToString ();
948 // pad formatted string and append to result
950 if (width > str.length) {
951 const char padchar = ' ';
952 int padlen = width - str.length;
956 result.Append (padchar, padlen);
959 result.Append (padchar, padlen);
968 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
969 result.Append (format, start, ptr - start - 1);
973 throw new FormatException ("Input string was not in a correct format.");
977 if (start < format.length)
978 result.Append (format, start, format.Length - start);
981 public unsafe static String Copy (String str)
984 throw new ArgumentNullException ("str");
986 int length = str.length;
988 String tmp = InternalAllocateStr (length);
990 fixed (char *dest = tmp, src = str) {
991 memcpy ((byte*)dest, (byte*)src, length * 2);
997 public static String Concat (Object obj)
1000 return String.Empty;
1002 return obj.ToString ();
1005 public unsafe static String Concat (Object obj1, Object obj2)
1009 s1 = (obj1 != null) ? obj1.ToString () : null;
1010 s2 = (obj2 != null) ? obj2.ToString () : null;
1014 return String.Empty;
1017 } else if (s2 == null)
1020 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1021 if (s1.Length != 0) {
1022 fixed (char *dest = tmp, src = s1) {
1023 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1026 if (s2.Length != 0) {
1027 fixed (char *dest = tmp, src = s2) {
1028 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1035 public static String Concat (Object obj1, Object obj2, Object obj3)
1041 s1 = obj1.ToString ();
1046 s2 = obj2.ToString ();
1051 s3 = obj3.ToString ();
1053 return Concat (s1, s2, s3);
1056 #if ! BOOTSTRAP_WITH_OLDLIB
1057 [CLSCompliant(false)]
1058 public static String Concat (Object obj1, Object obj2, Object obj3,
1059 Object obj4, __arglist)
1061 string s1, s2, s3, s4;
1066 s1 = obj1.ToString ();
1071 s2 = obj2.ToString ();
1076 s3 = obj3.ToString ();
1078 ArgIterator iter = new ArgIterator (__arglist);
1079 int argCount = iter.GetRemainingCount();
1081 StringBuilder sb = new StringBuilder ();
1083 sb.Append (obj4.ToString ());
1085 for (int i = 0; i < argCount; i++) {
1086 TypedReference typedRef = iter.GetNextArg ();
1087 sb.Append (TypedReference.ToObject (typedRef));
1090 s4 = sb.ToString ();
1092 return Concat (s1, s2, s3, s4);
1096 public unsafe static String Concat (String s1, String s2)
1100 return String.Empty;
1107 String tmp = InternalAllocateStr (s1.length + s2.length);
1109 if (s1.Length != 0) {
1110 fixed (char *dest = tmp, src = s1) {
1111 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1114 if (s2.Length != 0) {
1115 fixed (char *dest = tmp, src = s2) {
1116 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1123 public unsafe static String Concat (String s1, String s2, String s3)
1128 return String.Empty;
1147 //return InternalConcat (s1, s2, s3);
1148 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1150 if (s1.Length != 0) {
1151 fixed (char *dest = tmp, src = s1) {
1152 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1155 if (s2.Length != 0) {
1156 fixed (char *dest = tmp, src = s2) {
1157 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1160 if (s3.Length != 0) {
1161 fixed (char *dest = tmp, src = s3) {
1162 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1169 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1171 if (s1 == null && s2 == null && s3 == null && s4 == null)
1172 return String.Empty;
1183 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1185 if (s1.Length != 0) {
1186 fixed (char *dest = tmp, src = s1) {
1187 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1190 if (s2.Length != 0) {
1191 fixed (char *dest = tmp, src = s2) {
1192 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1195 if (s3.Length != 0) {
1196 fixed (char *dest = tmp, src = s3) {
1197 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1200 if (s4.Length != 0) {
1201 fixed (char *dest = tmp, src = s4) {
1202 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1209 public static String Concat (params Object[] args)
1212 throw new ArgumentNullException ("args");
1214 int i = args.Length;
1216 return String.Empty;
1218 string [] strings = new string [i];
1221 foreach (object arg in args) {
1223 strings[i] = String.Empty;
1225 strings[i] = arg.ToString ();
1226 len += strings[i].length;
1232 return String.Empty;
1234 return InternalJoin (String.Empty, strings, 0, strings.Length);
1237 public static String Concat (params String[] values)
1240 throw new ArgumentNullException ("values");
1242 return InternalJoin (String.Empty, values, 0, values.Length);
1245 public unsafe String Insert (int startIndex, String value)
1248 throw new ArgumentNullException ("value");
1250 if (startIndex < 0 || startIndex > this.length)
1251 throw new ArgumentOutOfRangeException ();
1253 if (value.Length == 0)
1255 if (this.Length == 0)
1257 String tmp = InternalAllocateStr (this.length + value.length);
1259 fixed (char *dest = tmp, src = this, val = value) {
1261 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1263 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1264 dst += value.length;
1265 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1271 public static string Intern (string str)
1274 throw new ArgumentNullException ("str");
1276 return InternalIntern (str);
1279 public static string IsInterned (string str)
1282 throw new ArgumentNullException ("str");
1284 return InternalIsInterned (str);
1287 public static string Join (string separator, string [] value)
1290 throw new ArgumentNullException ("value");
1292 return Join (separator, value, 0, value.Length);
1295 public static string Join (string separator, string[] value, int startIndex, int count)
1298 throw new ArgumentNullException ("value");
1300 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1302 throw new ArgumentOutOfRangeException ("count", "< 0");
1303 // re-ordered to avoid possible integer overflow
1304 if (startIndex > value.Length - count)
1305 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1307 if (startIndex == value.Length)
1308 return String.Empty;
1309 if (separator == null)
1310 separator = String.Empty;
1312 return InternalJoin (separator, value, startIndex, count);
1315 bool IConvertible.ToBoolean (IFormatProvider provider)
1317 return Convert.ToBoolean (this, provider);
1320 byte IConvertible.ToByte (IFormatProvider provider)
1322 return Convert.ToByte (this, provider);
1325 char IConvertible.ToChar (IFormatProvider provider)
1327 return Convert.ToChar (this, provider);
1330 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1332 return Convert.ToDateTime (this, provider);
1335 decimal IConvertible.ToDecimal (IFormatProvider provider)
1337 return Convert.ToDecimal (this, provider);
1340 double IConvertible.ToDouble (IFormatProvider provider)
1342 return Convert.ToDouble (this, provider);
1345 short IConvertible.ToInt16 (IFormatProvider provider)
1347 return Convert.ToInt16 (this, provider);
1350 int IConvertible.ToInt32 (IFormatProvider provider)
1352 return Convert.ToInt32 (this, provider);
1355 long IConvertible.ToInt64 (IFormatProvider provider)
1357 return Convert.ToInt64 (this, provider);
1360 sbyte IConvertible.ToSByte (IFormatProvider provider)
1362 return Convert.ToSByte (this, provider);
1365 float IConvertible.ToSingle (IFormatProvider provider)
1367 return Convert.ToSingle (this, provider);
1370 string IConvertible.ToString (IFormatProvider format)
1375 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1377 return Convert.ToType (this, conversionType, provider);
1380 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1382 return Convert.ToUInt16 (this, provider);
1385 uint IConvertible.ToUInt32 (IFormatProvider provider)
1387 return Convert.ToUInt32 (this, provider);
1390 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1392 return Convert.ToUInt64 (this, provider);
1395 TypeCode IConvertible.GetTypeCode ()
1397 return TypeCode.String;
1406 public CharEnumerator GetEnumerator ()
1408 return new CharEnumerator (this);
1411 IEnumerator IEnumerable.GetEnumerator ()
1413 return new CharEnumerator (this);
1416 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1417 out bool left_align, out string format)
1419 // parses format specifier of form:
1425 // N = argument number (non-negative integer)
1427 n = ParseDecimal (str, ref ptr);
1429 throw new FormatException ("Input string was not in a correct format.");
1431 // M = width (non-negative integer)
1433 if (str[ptr] == ',') {
1434 // White space between ',' and number or sign.
1436 while (Char.IsWhiteSpace (str [ptr]))
1439 format = str.Substring (start, ptr - start);
1441 left_align = (str [ptr] == '-');
1445 width = ParseDecimal (str, ref ptr);
1447 throw new FormatException ("Input string was not in a correct format.");
1455 // F = argument format (string)
1457 if (str[ptr] == ':') {
1459 while (str[ptr] != '}')
1462 format += str.Substring (start, ptr - start);
1467 if (str[ptr ++] != '}')
1468 throw new FormatException ("Input string was not in a correct format.");
1470 catch (IndexOutOfRangeException) {
1471 throw new FormatException ("Input string was not in a correct format.");
1475 private static int ParseDecimal (string str, ref int ptr)
1481 if (c < '0' || '9' < c)
1484 n = n * 10 + c - '0';
1495 internal unsafe void InternalSetChar (int idx, char val)
1497 if ((uint) idx >= (uint) Length)
1498 throw new ArgumentOutOfRangeException ("idx");
1500 fixed (char * pStr = &start_char)
1506 internal unsafe void InternalSetLength (int newLength)
1508 if (newLength > length)
1509 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1513 // zero terminate, we can pass string objects directly via pinvoke
1514 fixed (char * pStr = &start_char) {
1515 pStr [length] = '\0';
1520 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
1522 public unsafe override int GetHashCode ()
1524 fixed (char * c = this) {
1526 char * end = cc + length - 1;
1528 for (;cc < end; cc += 2) {
1529 h = (h << 5) - h + *cc;
1530 h = (h << 5) - h + cc [1];
1534 h = (h << 5) - h + *cc;
1539 /* helpers used by the runtime as well as above or eslewhere in corlib */
1540 internal static unsafe void memset (byte *dest, int val, int len)
1551 val = val | (val << 8);
1552 val = val | (val << 16);
1555 int rest = (int)dest & 3;
1563 } while (rest != 0);
1566 ((int*)dest) [0] = val;
1567 ((int*)dest) [1] = val;
1568 ((int*)dest) [2] = val;
1569 ((int*)dest) [3] = val;
1574 ((int*)dest) [0] = val;
1586 internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
1587 /*while (size >= 32) {
1588 // using long is better than int and slower than double
1589 // FIXME: enable this only on correct alignment or on platforms
1590 // that can tolerate unaligned reads/writes of doubles
1591 ((double*)dest) [0] = ((double*)src) [0];
1592 ((double*)dest) [1] = ((double*)src) [1];
1593 ((double*)dest) [2] = ((double*)src) [2];
1594 ((double*)dest) [3] = ((double*)src) [3];
1599 while (size >= 16) {
1600 ((int*)dest) [0] = ((int*)src) [0];
1601 ((int*)dest) [1] = ((int*)src) [1];
1602 ((int*)dest) [2] = ((int*)src) [2];
1603 ((int*)dest) [3] = ((int*)src) [3];
1609 ((int*)dest) [0] = ((int*)src) [0];
1615 ((byte*)dest) [0] = ((byte*)src) [0];
1621 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
1623 ((short*)dest) [0] = ((short*)src) [0];
1624 ((short*)dest) [1] = ((short*)src) [1];
1625 ((short*)dest) [2] = ((short*)src) [2];
1626 ((short*)dest) [3] = ((short*)src) [3];
1632 ((short*)dest) [0] = ((short*)src) [0];
1638 ((byte*)dest) [0] = ((byte*)src) [0];
1640 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
1642 ((byte*)dest) [0] = ((byte*)src) [0];
1643 ((byte*)dest) [1] = ((byte*)src) [1];
1644 ((byte*)dest) [2] = ((byte*)src) [2];
1645 ((byte*)dest) [3] = ((byte*)src) [3];
1646 ((byte*)dest) [4] = ((byte*)src) [4];
1647 ((byte*)dest) [5] = ((byte*)src) [5];
1648 ((byte*)dest) [6] = ((byte*)src) [6];
1649 ((byte*)dest) [7] = ((byte*)src) [7];
1655 ((byte*)dest) [0] = ((byte*)src) [0];
1656 ((byte*)dest) [1] = ((byte*)src) [1];
1662 ((byte*)dest) [0] = ((byte*)src) [0];
1664 static unsafe void memcpy (byte *dest, byte *src, int size) {
1665 // FIXME: if pointers are not aligned, try to align them
1666 // so a faster routine can be used. Handle the case where
1667 // the pointers can't be reduced to have the same alignment
1668 // (just ignore the issue on x86?)
1669 if ((((int)dest | (int)src) & 3) != 0) {
1670 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
1676 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
1677 ((short*)dest) [0] = ((short*)src) [0];
1682 if ((((int)dest | (int)src) & 1) != 0) {
1683 memcpy1 (dest, src, size);
1686 if ((((int)dest | (int)src) & 2) != 0) {
1687 memcpy2 (dest, src, size);
1691 memcpy4 (dest, src, size);
1694 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1695 unsafe public extern String (char *value);
1697 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1698 unsafe public extern String (char *value, int startIndex, int length);
1700 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1701 unsafe public extern String (sbyte *value);
1703 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1704 unsafe public extern String (sbyte *value, int startIndex, int length);
1706 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
1707 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
1709 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1710 public extern String (char [] val, int startIndex, int length);
1712 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1713 public extern String (char [] val);
1715 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1716 public extern String (char c, int count);
1718 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1719 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
1721 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1722 private extern String InternalReplace (char oldChar, char newChar);
1724 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1725 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
1727 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1728 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
1730 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1731 private extern String[] InternalSplit (char[] separator, int count);
1733 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1734 private extern String InternalTrim (char[] chars, int typ);
1736 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1737 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
1739 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1740 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
1742 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1743 private extern String InternalPad (int width, char chr, bool right);
1745 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1746 internal extern static String InternalAllocateStr (int length);
1748 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1749 internal extern static void InternalStrcpy (String dest, int destPos, String src);
1751 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1752 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
1754 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1755 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
1757 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1758 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
1760 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1761 private extern static string InternalIntern (string str);
1763 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1764 private extern static string InternalIsInterned (string str);