6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
40 // Ensure that argument checks on methods don't overflow
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
60 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
62 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
65 [NonSerialized] private int length;
66 [NonSerialized] private char start_char;
68 public static readonly String Empty = "";
70 public static unsafe bool Equals (string a, string b)
72 if ((a as object) == (b as object))
75 if (a == null || b == null)
83 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
88 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
89 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
90 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
91 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
100 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
101 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
110 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
118 return len == 0 || *s1_ptr == *s2_ptr;
122 public static bool operator == (String a, String b)
124 return Equals (a, b);
127 public static bool operator != (String a, String b)
129 return !Equals (a, b);
133 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
135 public override bool Equals (Object obj)
137 return Equals (this, obj as String);
141 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
143 public bool Equals (String value)
145 return Equals (this, value);
148 [IndexerName ("Chars")]
149 public unsafe char this [int index] {
151 if (index < 0 || index >= length)
152 throw new IndexOutOfRangeException ();
153 fixed (char* c = &start_char)
158 public Object Clone ()
163 public TypeCode GetTypeCode ()
165 return TypeCode.String;
168 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
170 if (destination == null)
171 throw new ArgumentNullException ("destination");
173 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
174 if (destinationIndex < 0)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
177 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
178 if (sourceIndex > Length - count)
179 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
180 if (destinationIndex > destination.Length - count)
181 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
183 fixed (char* dest = destination, src = this)
184 CharCopy (dest + destinationIndex, src + sourceIndex, count);
187 public unsafe char[] ToCharArray ()
189 char[] tmp = new char [length];
190 fixed (char* dest = tmp, src = this)
191 CharCopy (dest, src, length);
195 public unsafe char[] ToCharArray (int startIndex, int length)
198 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
200 throw new ArgumentOutOfRangeException ("length", "< 0");
201 if (startIndex > this.length - length)
202 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
204 char[] tmp = new char [length];
205 fixed (char* dest = tmp, src = this)
206 CharCopy (dest, src + startIndex, length);
210 public String [] Split (params char [] separator)
212 return Split (separator, Int32.MaxValue);
215 public String[] Split (char[] separator, int count)
217 if (separator == null || separator.Length == 0)
218 separator = WhiteChars;
221 throw new ArgumentOutOfRangeException ("count");
224 return new String[0];
227 return new String[1] { this };
229 return InternalSplit (separator, count, 0);
234 [MonoDocumentationNote ("code should be moved to managed")]
235 public String[] Split (char[] separator, int count, StringSplitOptions options)
237 if (separator == null || separator.Length == 0)
238 return Split (WhiteChars, count, options);
241 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
242 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
243 throw new ArgumentException ("Illegal enum value: " + options + ".");
246 return new string [0];
248 return InternalSplit (separator, count, (int)options);
252 public String[] Split (string[] separator, int count, StringSplitOptions options)
254 if (separator == null || separator.Length == 0)
255 return Split (WhiteChars, count, options);
258 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
259 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
260 throw new ArgumentException ("Illegal enum value: " + options + ".");
262 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
264 if (count == 0 || (this == String.Empty && removeEmpty))
265 return new String [0];
267 ArrayList arr = new ArrayList ();
271 while (pos < this.Length) {
273 int matchPos = Int32.MaxValue;
275 // Find the first position where any of the separators matches
276 for (int i = 0; i < separator.Length; ++i) {
277 string sep = separator [i];
278 if (sep == null || sep == String.Empty)
281 int match = IndexOf (sep, pos);
282 if (match > -1 && match < matchPos) {
288 if (matchIndex == -1)
291 if (!(matchPos == pos && removeEmpty))
292 arr.Add (this.Substring (pos, matchPos - pos));
294 pos = matchPos + separator [matchIndex].Length;
298 if (matchCount == count - 1)
303 return new String [] { this };
305 if (removeEmpty && pos == this.Length) {
306 String[] res = new String [arr.Count];
307 arr.CopyTo (0, res, 0, arr.Count);
312 String[] res = new String [arr.Count + 1];
313 arr.CopyTo (0, res, 0, arr.Count);
314 res [arr.Count] = this.Substring (pos);
322 public String[] Split (char[] separator, StringSplitOptions options)
324 return Split (separator, Int32.MaxValue, options);
328 public String[] Split (String[] separator, StringSplitOptions options)
330 return Split (separator, Int32.MaxValue, options);
334 public String Substring (int startIndex)
339 if (startIndex < 0 || startIndex > this.length)
340 throw new ArgumentOutOfRangeException ("startIndex");
343 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
345 if (startIndex > this.length)
346 throw new ArgumentOutOfRangeException ("length", "Cannot exceed length of string.");
349 return SubstringUnchecked (startIndex, this.length - startIndex);
352 public String Substring (int startIndex, int length)
355 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
357 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
359 if (startIndex > this.length)
360 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
362 if (startIndex > this.length - length)
363 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
365 // FIXME: this causes regressions in System.Xml
367 if (startIndex == 0 && length == this.length)
372 return SubstringUnchecked (startIndex, length);
375 internal unsafe String SubstringUnchecked (int startIndex, int length)
380 string tmp = InternalAllocateStr (length);
381 fixed (char* dest = tmp, src = this) {
382 CharCopy (dest, src + startIndex, length);
387 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
389 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
391 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
392 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
393 (char) 0x3000, (char) 0xFEFF };
395 public String Trim ()
399 int start = FindNotWhiteSpace (0, length, 1);
404 int end = FindNotWhiteSpace (length - 1, start, -1);
406 int newLength = end - start + 1;
407 if (newLength == length)
410 return SubstringUnchecked (start, newLength);
413 public String Trim (params char[] trimChars)
415 if (trimChars == null || trimChars.Length == 0)
420 int start = FindNotInTable (0, length, 1, trimChars);
425 int end = FindNotInTable (length - 1, start, -1, trimChars);
427 int newLength = end - start + 1;
428 if (newLength == length)
431 return SubstringUnchecked (start, newLength);
434 public String TrimStart (params char[] trimChars)
439 if (trimChars == null || trimChars.Length == 0)
440 start = FindNotWhiteSpace (0, length, 1);
442 start = FindNotInTable (0, length, 1, trimChars);
447 return SubstringUnchecked (start, length - start);
450 public String TrimEnd (params char[] trimChars)
455 if (trimChars == null || trimChars.Length == 0)
456 end = FindNotWhiteSpace (length - 1, -1, -1);
458 end = FindNotInTable (length - 1, -1, -1, trimChars);
464 return SubstringUnchecked (0, end);
467 private int FindNotWhiteSpace (int pos, int target, int change)
469 while (pos != target) {
473 if (c < 0x9 || c > 0xD)
478 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
480 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
482 if (c < 0x2000 || c > 0x200B)
491 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
493 fixed (char* tablePtr = table, thisPtr = this) {
494 while (pos != target) {
495 char c = thisPtr[pos];
497 while (x < table.Length) {
498 if (c == tablePtr[x])
502 if (x == table.Length)
510 public static int Compare (String strA, String strB)
512 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
515 public static int Compare (String strA, String strB, bool ignoreCase)
517 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
520 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
523 throw new ArgumentNullException ("culture");
525 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
528 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
530 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
533 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
535 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
538 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
541 throw new ArgumentNullException ("culture");
543 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
544 throw new ArgumentOutOfRangeException ();
556 else if (strB == null) {
560 CompareOptions compopts;
563 compopts = CompareOptions.IgnoreCase;
565 compopts = CompareOptions.None;
567 // Need to cap the requested length to the
568 // length of the string, because
569 // CompareInfo.Compare will insist that length
570 // <= (string.Length - offset)
575 if (length > (strA.Length - indexA)) {
576 len1 = strA.Length - indexA;
579 if (length > (strB.Length - indexB)) {
580 len2 = strB.Length - indexB;
583 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
584 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
587 public static int Compare (string strA, string strB, StringComparison comparisonType)
589 switch (comparisonType) {
590 case StringComparison.CurrentCulture:
591 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
592 case StringComparison.CurrentCultureIgnoreCase:
593 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
594 case StringComparison.InvariantCulture:
595 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
596 case StringComparison.InvariantCultureIgnoreCase:
597 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
598 case StringComparison.Ordinal:
599 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
600 case StringComparison.OrdinalIgnoreCase:
601 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
603 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
604 throw new ArgumentException (msg, "comparisonType");
608 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
610 switch (comparisonType) {
611 case StringComparison.CurrentCulture:
612 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
613 case StringComparison.CurrentCultureIgnoreCase:
614 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
615 case StringComparison.InvariantCulture:
616 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
617 case StringComparison.InvariantCultureIgnoreCase:
618 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
619 case StringComparison.Ordinal:
620 return CompareOrdinal (strA, indexA, strB, indexB, length);
621 case StringComparison.OrdinalIgnoreCase:
622 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
624 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
625 throw new ArgumentException (msg, "comparisonType");
629 public static bool Equals (string a, string b, StringComparison comparisonType)
631 return String.Compare (a, b, comparisonType) == 0;
634 public bool Equals (string value, StringComparison comparisonType)
636 return String.Compare (value, this, comparisonType) == 0;
639 public int CompareTo (Object value)
644 if (!(value is String))
645 throw new ArgumentException ();
647 return String.Compare (this, (String) value);
650 public int CompareTo (String strB)
655 return Compare (this, strB);
658 public static int CompareOrdinal (String strA, String strB)
660 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
663 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
665 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
666 throw new ArgumentOutOfRangeException ();
668 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
671 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
673 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
674 throw new ArgumentOutOfRangeException ();
676 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
679 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
686 } else if (strB == null) {
689 int lengthA = Math.Min (lenA, strA.Length - indexA);
690 int lengthB = Math.Min (lenB, strB.Length - indexB);
692 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
695 fixed (char* aptr = strA, bptr = strB) {
696 char* ap = aptr + indexA;
697 char* end = ap + Math.Min (lengthA, lengthB);
698 char* bp = bptr + indexB;
705 return lengthA - lengthB;
709 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
711 // Same as above, but checks versus uppercase characters
717 } else if (strB == null) {
720 int lengthA = Math.Min (lenA, strA.Length - indexA);
721 int lengthB = Math.Min (lenB, strB.Length - indexB);
723 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
726 fixed (char* aptr = strA, bptr = strB) {
727 char* ap = aptr + indexA;
728 char* end = ap + Math.Min (lengthA, lengthB);
729 char* bp = bptr + indexB;
732 char c1 = Char.ToUpperInvariant (*ap);
733 char c2 = Char.ToUpperInvariant (*bp);
740 return lengthA - lengthB;
744 public bool EndsWith (String value)
747 throw new ArgumentNullException ("value");
749 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
757 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
760 throw new ArgumentNullException ("value");
762 culture = CultureInfo.CurrentCulture;
764 return culture.CompareInfo.IsSuffix (this, value,
765 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
768 // Following methods are culture-insensitive
769 public int IndexOfAny (char [] anyOf)
772 throw new ArgumentNullException ();
773 if (this.length == 0)
776 return InternalIndexOfAny (anyOf, 0, this.length);
779 public int IndexOfAny (char [] anyOf, int startIndex)
782 throw new ArgumentNullException ();
783 if (startIndex < 0 || startIndex > this.length)
784 throw new ArgumentOutOfRangeException ();
786 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
789 public int IndexOfAny (char [] anyOf, int startIndex, int count)
792 throw new ArgumentNullException ();
793 if (startIndex < 0 || startIndex > this.length)
794 throw new ArgumentOutOfRangeException ();
795 // re-ordered to avoid possible integer overflow
796 if (count < 0 || startIndex > this.length - count)
797 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
799 return InternalIndexOfAny (anyOf, startIndex, count);
802 unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
804 if (anyOf.Length == 0)
807 if (anyOf.Length == 1)
808 return IndexOfImpl(anyOf[0], startIndex, count);
810 fixed (char* any = anyOf) {
814 char* end_any_ptr = any + anyOf.Length;
816 while (++any_ptr != end_any_ptr) {
817 if (*any_ptr > highest) {
822 if (*any_ptr < lowest)
826 fixed (char* start = &start_char) {
827 char* ptr = start + startIndex;
828 char* end_ptr = ptr + count;
830 while (ptr != end_ptr) {
831 if (*ptr > highest || *ptr < lowest) {
837 return (int)(ptr - start);
840 while (++any_ptr != end_any_ptr) {
841 if (*ptr == *any_ptr)
842 return (int)(ptr - start);
854 public int IndexOf (string value, StringComparison comparison)
856 return IndexOf (value, 0, this.Length, comparison);
859 public int IndexOf (string value, int startIndex, StringComparison comparison)
861 return IndexOf (value, startIndex, this.Length - startIndex, comparison);
864 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
866 switch (comparison) {
867 case StringComparison.CurrentCulture:
868 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
869 case StringComparison.CurrentCultureIgnoreCase:
870 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
871 case StringComparison.InvariantCulture:
872 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
873 case StringComparison.InvariantCultureIgnoreCase:
874 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
875 case StringComparison.Ordinal:
876 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
877 case StringComparison.OrdinalIgnoreCase:
878 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
881 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparison);
882 throw new ArgumentException (msg, "comparisonType");
885 public int LastIndexOf (string value, StringComparison comparison)
887 return LastIndexOf (value, value.Length - 1, value.Length, comparison);
890 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
892 return LastIndexOf (value, startIndex, startIndex + 1, comparison);
895 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
897 switch (comparison) {
898 case StringComparison.CurrentCulture:
899 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
900 case StringComparison.CurrentCultureIgnoreCase:
901 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
902 case StringComparison.InvariantCulture:
903 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
904 case StringComparison.InvariantCultureIgnoreCase:
905 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
906 case StringComparison.Ordinal:
907 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
908 case StringComparison.OrdinalIgnoreCase:
909 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
912 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparison);
913 throw new ArgumentException (msg, "comparison");
917 public int IndexOf (char value)
919 if (this.length == 0)
922 return IndexOfImpl (value, 0, this.length);
925 public int IndexOf (String value)
927 return IndexOf (value, 0, this.length);
930 public int IndexOf (char value, int startIndex)
932 return IndexOf (value, startIndex, this.length - startIndex);
935 public int IndexOf (String value, int startIndex)
937 return IndexOf (value, startIndex, this.length - startIndex);
940 /* This method is culture-insensitive */
941 public int IndexOf (char value, int startIndex, int count)
943 if (startIndex < 0 || startIndex > this.length)
944 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
946 throw new ArgumentOutOfRangeException ("count", "< 0");
947 // re-ordered to avoid possible integer overflow
948 if (startIndex > this.length - count)
949 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
951 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
954 return IndexOfImpl (value, startIndex, count);
957 unsafe int IndexOfImpl (char value, int startIndex, int count)
959 // It helps JIT compiler to optimize comparison
960 int value_32 = (int)value;
962 fixed (char* start = &start_char) {
963 char* ptr = start + startIndex;
964 char* end_ptr = ptr + (count >> 3 << 3);
966 while (ptr != end_ptr) {
967 if (*ptr == value_32)
968 return (int)(ptr - start);
969 if (ptr[1] == value_32)
970 return (int)(ptr - start + 1);
971 if (ptr[2] == value_32)
972 return (int)(ptr - start + 2);
973 if (ptr[3] == value_32)
974 return (int)(ptr - start + 3);
975 if (ptr[4] == value_32)
976 return (int)(ptr - start + 4);
977 if (ptr[5] == value_32)
978 return (int)(ptr - start + 5);
979 if (ptr[6] == value_32)
980 return (int)(ptr - start + 6);
981 if (ptr[7] == value_32)
982 return (int)(ptr - start + 7);
987 end_ptr += count & 0x07;
988 while (ptr != end_ptr) {
989 if (*ptr == value_32)
990 return (int)(ptr - start);
998 /* But this one is culture-sensitive */
999 public int IndexOf (String value, int startIndex, int count)
1003 throw new ArgumentNullException ("value");
1005 throw new ArgumentNullException ("string2");
1007 if (startIndex < 0 || startIndex > this.length)
1008 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1009 if (count < 0 || startIndex > this.length - count)
1010 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1012 if (value.length == 0)
1015 if (startIndex == 0 && this.length == 0)
1021 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1024 public int LastIndexOfAny (char [] anyOf)
1027 throw new ArgumentNullException ();
1029 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
1032 public int LastIndexOfAny (char [] anyOf, int startIndex)
1035 throw new ArgumentNullException ();
1037 if (startIndex < 0 || startIndex >= this.length)
1038 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1040 if (this.length == 0)
1043 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
1046 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1049 throw new ArgumentNullException ();
1051 if ((startIndex < 0) || (startIndex >= this.Length))
1052 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1053 if ((count < 0) || (count > this.Length))
1054 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1055 if (startIndex - count + 1 < 0)
1056 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1058 if (this.length == 0)
1061 return InternalLastIndexOfAny (anyOf, startIndex, count);
1064 public int LastIndexOf (char value)
1066 if (this.length == 0)
1069 return LastIndexOfImpl (value, this.length - 1, this.length);
1072 public int LastIndexOf (String value)
1074 if (this.length == 0)
1075 /* This overload does additional checking */
1076 return LastIndexOf (value, 0, 0);
1078 return LastIndexOf (value, this.length - 1, this.length);
1081 public int LastIndexOf (char value, int startIndex)
1083 return LastIndexOf (value, startIndex, startIndex + 1);
1086 public int LastIndexOf (String value, int startIndex)
1088 int max = startIndex;
1089 if (max < this.Length)
1091 return LastIndexOf (value, startIndex, max);
1094 /* This method is culture-insensitive */
1095 public int LastIndexOf (char value, int startIndex, int count)
1097 if (startIndex == 0 && this.length == 0)
1100 // >= for char (> for string)
1101 if ((startIndex < 0) || (startIndex >= this.Length))
1102 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1103 if ((count < 0) || (count > this.Length))
1104 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1105 if (startIndex - count + 1 < 0)
1106 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1108 return LastIndexOfImpl (value, startIndex, count);
1111 /* This method is culture-insensitive */
1112 unsafe int LastIndexOfImpl (char value, int startIndex, int count)
1114 // It helps JIT compiler to optimize comparison
1115 int value_32 = (int)value;
1117 fixed (char* start = &start_char) {
1118 char* ptr = start + startIndex;
1119 char* end_ptr = ptr - (count >> 3 << 3);
1121 while (ptr != end_ptr) {
1122 if (*ptr == value_32)
1123 return (int)(ptr - start);
1124 if (ptr[-1] == value_32)
1125 return (int)(ptr - start) - 1;
1126 if (ptr[-2] == value_32)
1127 return (int)(ptr - start) - 2;
1128 if (ptr[-3] == value_32)
1129 return (int)(ptr - start) - 3;
1130 if (ptr[-4] == value_32)
1131 return (int)(ptr - start) - 4;
1132 if (ptr[-5] == value_32)
1133 return (int)(ptr - start) - 5;
1134 if (ptr[-6] == value_32)
1135 return (int)(ptr - start) - 6;
1136 if (ptr[-7] == value_32)
1137 return (int)(ptr - start) - 7;
1142 end_ptr -= count & 0x07;
1143 while (ptr != end_ptr) {
1144 if (*ptr == value_32)
1145 return (int)(ptr - start);
1153 /* But this one is culture-sensitive */
1154 public int LastIndexOf (String value, int startIndex, int count)
1158 throw new ArgumentNullException ("value");
1160 throw new ArgumentNullException ("string2");
1163 // -1 > startIndex > for string (0 > startIndex >= for char)
1164 if ((startIndex < -1) || (startIndex > this.Length))
1165 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1166 if ((count < 0) || (count > this.Length))
1167 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1168 if (startIndex - count + 1 < 0)
1169 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1171 if (value.Length == 0)
1174 if (startIndex == 0 && this.length == 0)
1177 // This check is needed to match undocumented MS behaviour
1178 if (this.length == 0 && value.length > 0)
1184 if (startIndex == this.Length)
1186 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1190 public bool Contains (String value)
1192 return IndexOf (value) != -1;
1195 public static bool IsNullOrEmpty (String value)
1197 return (value == null) || (value.Length == 0);
1200 public string Normalize ()
1202 return Normalization.Normalize (this, 0);
1205 public string Normalize (NormalizationForm form)
1209 return Normalization.Normalize (this, 0);
1210 case NormalizationForm.FormD:
1211 return Normalization.Normalize (this, 1);
1212 case NormalizationForm.FormKC:
1213 return Normalization.Normalize (this, 2);
1214 case NormalizationForm.FormKD:
1215 return Normalization.Normalize (this, 3);
1219 public bool IsNormalized ()
1221 return Normalization.IsNormalized (this, 0);
1224 public bool IsNormalized (NormalizationForm form)
1228 return Normalization.IsNormalized (this, 0);
1229 case NormalizationForm.FormD:
1230 return Normalization.IsNormalized (this, 1);
1231 case NormalizationForm.FormKC:
1232 return Normalization.IsNormalized (this, 2);
1233 case NormalizationForm.FormKD:
1234 return Normalization.IsNormalized (this, 3);
1238 public string Remove (int startIndex)
1241 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1242 if (startIndex >= this.length)
1243 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1245 return Remove (startIndex, this.length - startIndex);
1249 public String PadLeft (int totalWidth)
1251 return PadLeft (totalWidth, ' ');
1254 public unsafe String PadLeft (int totalWidth, char paddingChar)
1256 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1259 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1261 if (totalWidth < this.length)
1264 String tmp = InternalAllocateStr (totalWidth);
1266 fixed (char* dest = tmp, src = this) {
1267 char* padPos = dest;
1268 char* padTo = dest + (totalWidth - length);
1269 while (padPos != padTo)
1270 *padPos++ = paddingChar;
1272 CharCopy (padTo, src, length);
1277 public String PadRight (int totalWidth)
1279 return PadRight (totalWidth, ' ');
1282 public unsafe String PadRight (int totalWidth, char paddingChar)
1284 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1287 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1289 if (totalWidth < this.length)
1292 String tmp = InternalAllocateStr (totalWidth);
1294 fixed (char* dest = tmp, src = this) {
1295 CharCopy (dest, src, length);
1297 char* padPos = dest + length;
1298 char* padTo = dest + totalWidth;
1299 while (padPos != padTo)
1300 *padPos++ = paddingChar;
1305 public bool StartsWith (String value)
1308 throw new ArgumentNullException ("value");
1310 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1314 [ComVisible (false)]
1315 public bool StartsWith (string value, StringComparison comparisonType)
1318 throw new ArgumentNullException ("value");
1320 switch (comparisonType) {
1321 case StringComparison.CurrentCulture:
1322 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1323 case StringComparison.CurrentCultureIgnoreCase:
1324 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1325 case StringComparison.InvariantCulture:
1326 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1327 case StringComparison.InvariantCultureIgnoreCase:
1328 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1329 case StringComparison.Ordinal:
1330 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1331 case StringComparison.OrdinalIgnoreCase:
1332 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1334 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1335 throw new ArgumentException (msg, "comparisonType");
1339 [ComVisible (false)]
1340 public bool EndsWith (string value, StringComparison comparisonType)
1343 throw new ArgumentNullException ("value");
1345 switch (comparisonType) {
1346 case StringComparison.CurrentCulture:
1347 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1348 case StringComparison.CurrentCultureIgnoreCase:
1349 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1350 case StringComparison.InvariantCulture:
1351 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1352 case StringComparison.InvariantCultureIgnoreCase:
1353 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1354 case StringComparison.Ordinal:
1355 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1356 case StringComparison.OrdinalIgnoreCase:
1357 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1359 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1360 throw new ArgumentException (msg, "comparisonType");
1370 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1372 if (culture == null)
1373 culture = CultureInfo.CurrentCulture;
1375 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1378 /* This method is culture insensitive */
1379 public unsafe String Replace (char oldChar, char newChar)
1381 if (this.length == 0 || oldChar == newChar)
1384 int start_pos = IndexOfImpl (oldChar, 0, this.length);
1385 if (start_pos == -1)
1391 string tmp = InternalAllocateStr(length);
1392 fixed (char* dest = tmp, src = &start_char) {
1394 memcpy((byte*)dest, (byte*)src, start_pos * 2);
1396 char* end_ptr = dest + length;
1397 char* dest_ptr = dest + start_pos;
1398 char* src_ptr = src + start_pos;
1400 while (dest_ptr != end_ptr) {
1401 if (*src_ptr == oldChar)
1402 *dest_ptr = newChar;
1404 *dest_ptr = *src_ptr;
1413 /* This method is culture sensitive */
1414 public String Replace (String oldValue, String newValue)
1416 if (oldValue == null)
1417 throw new ArgumentNullException ("oldValue");
1419 if (oldValue.Length == 0)
1420 throw new ArgumentException ("oldValue is the empty string.");
1422 if (this.Length == 0)
1425 if (newValue == null)
1426 newValue = String.Empty;
1428 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1431 public unsafe String Remove (int startIndex, int count)
1434 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1436 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1437 if (startIndex > this.length - count)
1438 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1440 String tmp = InternalAllocateStr (this.length - count);
1442 fixed (char *dest = tmp, src = this) {
1444 CharCopy (dst, src, startIndex);
1445 int skip = startIndex + count;
1447 CharCopy (dst, src + skip, length - skip);
1452 public String ToLower ()
1454 return ToLower (CultureInfo.CurrentCulture);
1457 public String ToLower (CultureInfo culture)
1459 if (culture == null)
1460 throw new ArgumentNullException ("culture");
1462 if (culture.LCID == 0x007F) // Invariant
1463 return ToLowerInvariant ();
1465 return culture.TextInfo.ToLower (this);
1469 public unsafe String ToLowerInvariant ()
1471 internal unsafe String ToLowerInvariant ()
1474 string tmp = InternalAllocateStr (length);
1475 fixed (char* source = &start_char, dest = tmp) {
1477 char* destPtr = (char*)dest;
1478 char* sourcePtr = (char*)source;
1480 for (int n = 0; n < length; n++) {
1481 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1489 public String ToUpper ()
1491 return ToUpper (CultureInfo.CurrentCulture);
1494 public String ToUpper (CultureInfo culture)
1496 if (culture == null)
1497 throw new ArgumentNullException ("culture");
1499 if (culture.LCID == 0x007F) // Invariant
1500 return ToUpperInvariant ();
1502 return culture.TextInfo.ToUpper (this);
1506 public unsafe String ToUpperInvariant ()
1508 internal unsafe String ToUpperInvariant ()
1511 string tmp = InternalAllocateStr (length);
1512 fixed (char* source = &start_char, dest = tmp) {
1514 char* destPtr = (char*)dest;
1515 char* sourcePtr = (char*)source;
1517 for (int n = 0; n < length; n++) {
1518 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1526 public override String ToString ()
1531 public String ToString (IFormatProvider provider)
1536 public static String Format (String format, Object arg0)
1538 return Format (null, format, new Object[] {arg0});
1541 public static String Format (String format, Object arg0, Object arg1)
1543 return Format (null, format, new Object[] {arg0, arg1});
1546 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1548 return Format (null, format, new Object[] {arg0, arg1, arg2});
1551 public static string Format (string format, params object[] args)
1553 return Format (null, format, args);
1556 public static string Format (IFormatProvider provider, string format, params object[] args)
1558 StringBuilder b = new StringBuilder ();
1559 FormatHelper (b, provider, format, args);
1560 return b.ToString ();
1563 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1566 throw new ArgumentNullException ("format");
1568 throw new ArgumentNullException ("args");
1572 while (ptr < format.length) {
1573 char c = format[ptr ++];
1576 result.Append (format, start, ptr - start - 1);
1578 // check for escaped open bracket
1580 if (format[ptr] == '{') {
1591 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1592 if (n >= args.Length)
1593 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1597 object arg = args[n];
1600 ICustomFormatter formatter = null;
1601 if (provider != null)
1602 formatter = provider.GetFormat (typeof (ICustomFormatter))
1603 as ICustomFormatter;
1606 else if (formatter != null)
1607 str = formatter.Format (arg_format, arg, provider);
1608 else if (arg is IFormattable)
1609 str = ((IFormattable)arg).ToString (arg_format, provider);
1611 str = arg.ToString ();
1613 // pad formatted string and append to result
1615 if (width > str.length) {
1616 const char padchar = ' ';
1617 int padlen = width - str.length;
1620 result.Append (str);
1621 result.Append (padchar, padlen);
1624 result.Append (padchar, padlen);
1625 result.Append (str);
1629 result.Append (str);
1633 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1634 result.Append (format, start, ptr - start - 1);
1637 else if (c == '}') {
1638 throw new FormatException ("Input string was not in a correct format.");
1642 if (start < format.length)
1643 result.Append (format, start, format.Length - start);
1646 public unsafe static String Copy (String str)
1649 throw new ArgumentNullException ("str");
1651 int length = str.length;
1653 String tmp = InternalAllocateStr (length);
1655 fixed (char *dest = tmp, src = str) {
1656 CharCopy (dest, src, length);
1662 public static String Concat (Object obj)
1665 return String.Empty;
1667 return obj.ToString ();
1670 public unsafe static String Concat (Object obj1, Object obj2)
1674 s1 = (obj1 != null) ? obj1.ToString () : null;
1675 s2 = (obj2 != null) ? obj2.ToString () : null;
1679 return String.Empty;
1682 } else if (s2 == null)
1685 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1686 if (s1.Length != 0) {
1687 fixed (char *dest = tmp, src = s1) {
1688 CharCopy (dest, src, s1.length);
1691 if (s2.Length != 0) {
1692 fixed (char *dest = tmp, src = s2) {
1693 CharCopy (dest + s1.Length, src, s2.length);
1700 public static String Concat (Object obj1, Object obj2, Object obj3)
1706 s1 = obj1.ToString ();
1711 s2 = obj2.ToString ();
1716 s3 = obj3.ToString ();
1718 return Concat (s1, s2, s3);
1721 #if ! BOOTSTRAP_WITH_OLDLIB
1722 [CLSCompliant(false)]
1723 public static String Concat (Object obj1, Object obj2, Object obj3,
1724 Object obj4, __arglist)
1726 string s1, s2, s3, s4;
1731 s1 = obj1.ToString ();
1736 s2 = obj2.ToString ();
1741 s3 = obj3.ToString ();
1743 ArgIterator iter = new ArgIterator (__arglist);
1744 int argCount = iter.GetRemainingCount();
1746 StringBuilder sb = new StringBuilder ();
1748 sb.Append (obj4.ToString ());
1750 for (int i = 0; i < argCount; i++) {
1751 TypedReference typedRef = iter.GetNextArg ();
1752 sb.Append (TypedReference.ToObject (typedRef));
1755 s4 = sb.ToString ();
1757 return Concat (s1, s2, s3, s4);
1761 public unsafe static String Concat (String s1, String s2)
1763 if (s1 == null || s1.Length == 0) {
1764 if (s2 == null || s2.Length == 0)
1765 return String.Empty;
1769 if (s2 == null || s2.Length == 0)
1772 String tmp = InternalAllocateStr (s1.length + s2.length);
1774 fixed (char *dest = tmp, src = s1)
1775 CharCopy (dest, src, s1.length);
1776 fixed (char *dest = tmp, src = s2)
1777 CharCopy (dest + s1.Length, src, s2.length);
1782 public unsafe static String Concat (String s1, String s2, String s3)
1784 if (s1 == null || s1.Length == 0){
1785 if (s2 == null || s2.Length == 0){
1786 if (s3 == null || s3.Length == 0)
1787 return String.Empty;
1790 if (s3 == null || s3.Length == 0)
1795 if (s2 == null || s2.Length == 0){
1796 if (s3 == null || s3.Length == 0)
1801 if (s3 == null || s3.Length == 0)
1806 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1808 if (s1.Length != 0) {
1809 fixed (char *dest = tmp, src = s1) {
1810 CharCopy (dest, src, s1.length);
1813 if (s2.Length != 0) {
1814 fixed (char *dest = tmp, src = s2) {
1815 CharCopy (dest + s1.Length, src, s2.length);
1818 if (s3.Length != 0) {
1819 fixed (char *dest = tmp, src = s3) {
1820 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
1827 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1829 if (s1 == null && s2 == null && s3 == null && s4 == null)
1830 return String.Empty;
1841 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1843 if (s1.Length != 0) {
1844 fixed (char *dest = tmp, src = s1) {
1845 CharCopy (dest, src, s1.length);
1848 if (s2.Length != 0) {
1849 fixed (char *dest = tmp, src = s2) {
1850 CharCopy (dest + s1.Length, src, s2.length);
1853 if (s3.Length != 0) {
1854 fixed (char *dest = tmp, src = s3) {
1855 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
1858 if (s4.Length != 0) {
1859 fixed (char *dest = tmp, src = s4) {
1860 CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
1867 public static String Concat (params Object[] args)
1870 throw new ArgumentNullException ("args");
1872 int argLen = args.Length;
1874 return String.Empty;
1876 string [] strings = new string [argLen];
1878 for (int i = 0; i < argLen; i++) {
1879 if (args[i] != null) {
1880 strings[i] = args[i].ToString ();
1881 len += strings[i].length;
1885 return String.Empty;
1887 return ConcatInternal (strings, len);
1890 public static String Concat (params String[] values)
1893 throw new ArgumentNullException ("values");
1896 for (int i = 0; i < values.Length; i++) {
1897 String s = values[i];
1902 return String.Empty;
1904 return ConcatInternal (values, len);
1907 private static unsafe String ConcatInternal (String[] values, int length)
1909 String tmp = InternalAllocateStr (length);
1911 fixed (char* dest = tmp) {
1913 for (int i = 0; i < values.Length; i++) {
1914 String source = values[i];
1915 if (source != null) {
1916 fixed (char* src = source) {
1917 CharCopy (dest + pos, src, source.length);
1919 pos += source.Length;
1926 public unsafe String Insert (int startIndex, String value)
1929 throw new ArgumentNullException ("value");
1931 if (startIndex < 0 || startIndex > this.length)
1932 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
1934 if (value.Length == 0)
1936 if (this.Length == 0)
1938 String tmp = InternalAllocateStr (this.length + value.length);
1940 fixed (char *dest = tmp, src = this, val = value) {
1942 CharCopy (dst, src, startIndex);
1944 CharCopy (dst, val, value.length);
1945 dst += value.length;
1946 CharCopy (dst, src + startIndex, length - startIndex);
1951 public static string Intern (string str)
1954 throw new ArgumentNullException ("str");
1956 return InternalIntern (str);
1959 public static string IsInterned (string str)
1962 throw new ArgumentNullException ("str");
1964 return InternalIsInterned (str);
1967 public static string Join (string separator, string [] value)
1970 throw new ArgumentNullException ("value");
1971 if (separator == null)
1972 separator = String.Empty;
1974 return JoinUnchecked (separator, value, 0, value.Length);
1977 public static string Join (string separator, string[] value, int startIndex, int count)
1980 throw new ArgumentNullException ("value");
1982 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1984 throw new ArgumentOutOfRangeException ("count", "< 0");
1985 if (startIndex > value.Length - count)
1986 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
1988 if (startIndex == value.Length)
1989 return String.Empty;
1990 if (separator == null)
1991 separator = String.Empty;
1993 return JoinUnchecked (separator, value, startIndex, count);
1996 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
1998 // Unchecked parameters
1999 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2000 // separator and value must not be null
2003 int maxIndex = startIndex + count;
2004 // Precount the number of characters that the resulting string will have
2005 for (int i = startIndex; i < maxIndex; i++) {
2006 String s = value[i];
2010 length += separator.length * (count - 1);
2012 return String.Empty;
2014 String tmp = InternalAllocateStr (length);
2017 fixed (char* dest = tmp, sepsrc = separator) {
2018 // Copy each string from value except the last one and add a separator for each
2020 for (int i = startIndex; i < maxIndex; i++) {
2021 String source = value[i];
2022 if (source != null) {
2023 if (source.Length > 0) {
2024 fixed (char* src = source)
2025 CharCopy (dest + pos, src, source.Length);
2026 pos += source.Length;
2029 if (separator.Length > 0) {
2030 CharCopy (dest + pos, sepsrc, separator.Length);
2031 pos += separator.Length;
2034 // Append last string that does not get an additional separator
2035 String sourceLast = value[maxIndex];
2036 if (sourceLast != null) {
2037 if (sourceLast.Length > 0) {
2038 fixed (char* src = sourceLast)
2039 CharCopy (dest + pos, src, sourceLast.Length);
2046 bool IConvertible.ToBoolean (IFormatProvider provider)
2048 return Convert.ToBoolean (this, provider);
2051 byte IConvertible.ToByte (IFormatProvider provider)
2053 return Convert.ToByte (this, provider);
2056 char IConvertible.ToChar (IFormatProvider provider)
2058 return Convert.ToChar (this, provider);
2061 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2063 return Convert.ToDateTime (this, provider);
2066 decimal IConvertible.ToDecimal (IFormatProvider provider)
2068 return Convert.ToDecimal (this, provider);
2071 double IConvertible.ToDouble (IFormatProvider provider)
2073 return Convert.ToDouble (this, provider);
2076 short IConvertible.ToInt16 (IFormatProvider provider)
2078 return Convert.ToInt16 (this, provider);
2081 int IConvertible.ToInt32 (IFormatProvider provider)
2083 return Convert.ToInt32 (this, provider);
2086 long IConvertible.ToInt64 (IFormatProvider provider)
2088 return Convert.ToInt64 (this, provider);
2091 sbyte IConvertible.ToSByte (IFormatProvider provider)
2093 return Convert.ToSByte (this, provider);
2096 float IConvertible.ToSingle (IFormatProvider provider)
2098 return Convert.ToSingle (this, provider);
2101 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2103 return Convert.ToType (this, conversionType, provider);
2106 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2108 return Convert.ToUInt16 (this, provider);
2111 uint IConvertible.ToUInt32 (IFormatProvider provider)
2113 return Convert.ToUInt32 (this, provider);
2116 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2118 return Convert.ToUInt64 (this, provider);
2127 public CharEnumerator GetEnumerator ()
2129 return new CharEnumerator (this);
2133 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2135 return new CharEnumerator (this);
2139 IEnumerator IEnumerable.GetEnumerator ()
2141 return new CharEnumerator (this);
2144 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2145 out bool left_align, out string format)
2147 // parses format specifier of form:
2153 // N = argument number (non-negative integer)
2155 n = ParseDecimal (str, ref ptr);
2157 throw new FormatException ("Input string was not in a correct format.");
2159 // M = width (non-negative integer)
2161 if (str[ptr] == ',') {
2162 // White space between ',' and number or sign.
2164 while (Char.IsWhiteSpace (str [ptr]))
2168 format = str.Substring (start, ptr - start);
2170 left_align = (str [ptr] == '-');
2174 width = ParseDecimal (str, ref ptr);
2176 throw new FormatException ("Input string was not in a correct format.");
2181 format = String.Empty;
2184 // F = argument format (string)
2186 if (str[ptr] == ':') {
2188 while (str[ptr] != '}')
2191 format += str.Substring (start, ptr - start);
2196 if (str[ptr ++] != '}')
2197 throw new FormatException ("Input string was not in a correct format.");
2199 catch (IndexOutOfRangeException) {
2200 throw new FormatException ("Input string was not in a correct format.");
2204 private static int ParseDecimal (string str, ref int ptr)
2210 if (c < '0' || '9' < c)
2213 n = n * 10 + c - '0';
2224 internal unsafe void InternalSetChar (int idx, char val)
2226 if ((uint) idx >= (uint) Length)
2227 throw new ArgumentOutOfRangeException ("idx");
2229 fixed (char * pStr = &start_char)
2235 internal unsafe void InternalSetLength (int newLength)
2237 if (newLength > length)
2238 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2240 // zero terminate, we can pass string objects directly via pinvoke
2241 // we also zero the rest of the string, since the new GC needs to be
2242 // able to handle the changing size (it will skip the 0 bytes).
2243 fixed (char * pStr = &start_char) {
2244 char *p = pStr + newLength;
2245 char *end = pStr + length;
2255 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2257 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2258 public unsafe override int GetHashCode ()
2260 fixed (char * c = this) {
2262 char * end = cc + length - 1;
2264 for (;cc < end; cc += 2) {
2265 h = (h << 5) - h + *cc;
2266 h = (h << 5) - h + cc [1];
2270 h = (h << 5) - h + *cc;
2275 internal unsafe int GetCaseInsensitiveHashCode ()
2277 fixed (char * c = this) {
2279 char * end = cc + length - 1;
2281 for (;cc < end; cc += 2) {
2282 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2283 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2287 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2292 // Certain constructors are redirected to CreateString methods with
2293 // matching argument list. The this pointer should not be used.
2295 private unsafe String CreateString (sbyte* value)
2298 return String.Empty;
2300 byte* bytes = (byte*) value;
2304 while (bytes++ [0] != 0)
2306 } catch (NullReferenceException) {
2307 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2309 } catch (AccessViolationException) {
2310 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2314 return CreateString (value, 0, length, null);
2317 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2319 return CreateString (value, startIndex, length, null);
2322 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2325 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2327 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2328 if (value + startIndex < value)
2329 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2331 bool isDefaultEncoding;
2333 if (isDefaultEncoding = (enc == null)) {
2336 throw new ArgumentNullException ("value");
2339 if (value == null || length == 0)
2341 return String.Empty;
2343 enc = Encoding.Default;
2346 byte [] bytes = new byte [length];
2349 fixed (byte* bytePtr = bytes)
2351 memcpy (bytePtr, (byte*) (value + startIndex), length);
2352 } catch (NullReferenceException) {
2354 if (!isDefaultEncoding)
2358 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2360 } catch (AccessViolationException) {
2361 if (!isDefaultEncoding)
2364 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2368 // GetString () is called even when length == 0
2369 return enc.GetString (bytes);
2372 unsafe string CreateString (char *value)
2375 return string.Empty;
2382 string result = InternalAllocateStr (i);
2385 fixed (char *dest = result) {
2386 CharCopy (dest, value, i);
2392 unsafe string CreateString (char *value, int startIndex, int length)
2395 return string.Empty;
2397 throw new ArgumentNullException ("value");
2399 throw new ArgumentOutOfRangeException ("startIndex");
2401 throw new ArgumentOutOfRangeException ("length");
2403 string result = InternalAllocateStr (length);
2405 fixed (char *dest = result) {
2406 CharCopy (dest, value + startIndex, length);
2411 unsafe string CreateString (char [] val, int startIndex, int length)
2414 throw new ArgumentNullException ("value");
2416 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2418 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2419 if (startIndex > val.Length - length)
2420 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2422 return string.Empty;
2424 string result = InternalAllocateStr (length);
2426 fixed (char *dest = result, src = val) {
2427 CharCopy (dest, src + startIndex, length);
2432 unsafe string CreateString (char [] val)
2435 return string.Empty;
2436 if (val.Length == 0)
2437 return string.Empty;
2438 string result = InternalAllocateStr (val.Length);
2440 fixed (char *dest = result, src = val) {
2441 CharCopy (dest, src, val.Length);
2446 unsafe string CreateString (char c, int count)
2449 throw new ArgumentOutOfRangeException ("count");
2451 return string.Empty;
2452 string result = InternalAllocateStr (count);
2453 fixed (char *dest = result) {
2455 char *end = p + count;
2464 /* helpers used by the runtime as well as above or eslewhere in corlib */
2465 internal static unsafe void memset (byte *dest, int val, int len)
2476 val = val | (val << 8);
2477 val = val | (val << 16);
2480 int rest = (int)dest & 3;
2488 } while (rest != 0);
2491 ((int*)dest) [0] = val;
2492 ((int*)dest) [1] = val;
2493 ((int*)dest) [2] = val;
2494 ((int*)dest) [3] = val;
2499 ((int*)dest) [0] = val;
2511 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2512 /*while (size >= 32) {
2513 // using long is better than int and slower than double
2514 // FIXME: enable this only on correct alignment or on platforms
2515 // that can tolerate unaligned reads/writes of doubles
2516 ((double*)dest) [0] = ((double*)src) [0];
2517 ((double*)dest) [1] = ((double*)src) [1];
2518 ((double*)dest) [2] = ((double*)src) [2];
2519 ((double*)dest) [3] = ((double*)src) [3];
2524 while (size >= 16) {
2525 ((int*)dest) [0] = ((int*)src) [0];
2526 ((int*)dest) [1] = ((int*)src) [1];
2527 ((int*)dest) [2] = ((int*)src) [2];
2528 ((int*)dest) [3] = ((int*)src) [3];
2534 ((int*)dest) [0] = ((int*)src) [0];
2540 ((byte*)dest) [0] = ((byte*)src) [0];
2546 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2548 ((short*)dest) [0] = ((short*)src) [0];
2549 ((short*)dest) [1] = ((short*)src) [1];
2550 ((short*)dest) [2] = ((short*)src) [2];
2551 ((short*)dest) [3] = ((short*)src) [3];
2557 ((short*)dest) [0] = ((short*)src) [0];
2563 ((byte*)dest) [0] = ((byte*)src) [0];
2565 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2567 ((byte*)dest) [0] = ((byte*)src) [0];
2568 ((byte*)dest) [1] = ((byte*)src) [1];
2569 ((byte*)dest) [2] = ((byte*)src) [2];
2570 ((byte*)dest) [3] = ((byte*)src) [3];
2571 ((byte*)dest) [4] = ((byte*)src) [4];
2572 ((byte*)dest) [5] = ((byte*)src) [5];
2573 ((byte*)dest) [6] = ((byte*)src) [6];
2574 ((byte*)dest) [7] = ((byte*)src) [7];
2580 ((byte*)dest) [0] = ((byte*)src) [0];
2581 ((byte*)dest) [1] = ((byte*)src) [1];
2587 ((byte*)dest) [0] = ((byte*)src) [0];
2590 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2591 // FIXME: if pointers are not aligned, try to align them
2592 // so a faster routine can be used. Handle the case where
2593 // the pointers can't be reduced to have the same alignment
2594 // (just ignore the issue on x86?)
2595 if ((((int)dest | (int)src) & 3) != 0) {
2596 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2602 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2603 ((short*)dest) [0] = ((short*)src) [0];
2608 if ((((int)dest | (int)src) & 1) != 0) {
2609 memcpy1 (dest, src, size);
2612 if ((((int)dest | (int)src) & 2) != 0) {
2613 memcpy2 (dest, src, size);
2617 memcpy4 (dest, src, size);
2620 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2621 // Same rules as for memcpy, but with the premise that
2622 // chars can only be aligned to even addresses if their
2623 // enclosing types are correctly aligned
2624 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2625 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2626 ((short*)dest) [0] = ((short*)src) [0];
2631 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2632 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2636 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2639 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2643 for (int i = count; i > 0; i--) {
2650 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2652 fixed (char* dest = target, src = source)
2653 CharCopy (dest + targetIndex, src + sourceIndex, count);
2656 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2658 fixed (char* dest = target, src = source)
2659 CharCopy (dest + targetIndex, src + sourceIndex, count);
2662 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2663 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2665 fixed (char* dest = target, src = source)
2666 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2669 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2670 unsafe public extern String (char *value);
2672 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2673 unsafe public extern String (char *value, int startIndex, int length);
2675 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2676 unsafe public extern String (sbyte *value);
2678 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2679 unsafe public extern String (sbyte *value, int startIndex, int length);
2681 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2682 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2684 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2685 public extern String (char [] val, int startIndex, int length);
2687 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2688 public extern String (char [] val);
2690 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2691 public extern String (char c, int count);
2693 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2694 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2696 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2697 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2699 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2700 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2702 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2703 private extern String[] InternalSplit (char[] separator, int count, int options);
2705 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2706 private extern String InternalTrim (char[] chars, int typ);
2708 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2709 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2711 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2712 private extern String InternalPad (int width, char chr, bool right);
2714 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2715 internal extern static String InternalAllocateStr (int length);
2717 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2718 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2720 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2721 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2723 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2724 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2726 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2727 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2729 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2730 private extern static string InternalIntern (string str);
2732 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2733 private extern static string InternalIsInterned (string str);