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 if (startIndex == 0 && length == this.length)
369 return SubstringUnchecked (startIndex, length);
372 // This method is used by StringBuilder.ToString() and is expected to
373 // always create a new string object (or return String.Empty).
374 internal unsafe String SubstringUnchecked (int startIndex, int length)
379 string tmp = InternalAllocateStr (length);
380 fixed (char* dest = tmp, src = this) {
381 CharCopy (dest, src + startIndex, length);
386 private static readonly char[] WhiteChars = {
387 (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,
396 (char) 0x202f, (char) 0x205f,
400 public String Trim ()
404 int start = FindNotWhiteSpace (0, length, 1);
409 int end = FindNotWhiteSpace (length - 1, start, -1);
411 int newLength = end - start + 1;
412 if (newLength == length)
415 return SubstringUnchecked (start, newLength);
418 public String Trim (params char[] trimChars)
420 if (trimChars == null || trimChars.Length == 0)
425 int start = FindNotInTable (0, length, 1, trimChars);
430 int end = FindNotInTable (length - 1, start, -1, trimChars);
432 int newLength = end - start + 1;
433 if (newLength == length)
436 return SubstringUnchecked (start, newLength);
439 public String TrimStart (params char[] trimChars)
444 if (trimChars == null || trimChars.Length == 0)
445 start = FindNotWhiteSpace (0, length, 1);
447 start = FindNotInTable (0, length, 1, trimChars);
452 return SubstringUnchecked (start, length - start);
455 public String TrimEnd (params char[] trimChars)
460 if (trimChars == null || trimChars.Length == 0)
461 end = FindNotWhiteSpace (length - 1, -1, -1);
463 end = FindNotInTable (length - 1, -1, -1, trimChars);
469 return SubstringUnchecked (0, end);
472 private int FindNotWhiteSpace (int pos, int target, int change)
474 while (pos != target) {
478 if (c < 0x9 || c > 0xD)
483 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
484 #if NET_2_0 || NET_2_1
485 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029
487 // On Silverlight this whitespace participates in Trim
488 && c != 0x202f && c != 0x205f
492 if (c < 0x2000 || c > 0x200B)
501 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
503 fixed (char* tablePtr = table, thisPtr = this) {
504 while (pos != target) {
505 char c = thisPtr[pos];
507 while (x < table.Length) {
508 if (c == tablePtr[x])
512 if (x == table.Length)
520 public static int Compare (String strA, String strB)
522 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
525 public static int Compare (String strA, String strB, bool ignoreCase)
527 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
530 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
533 throw new ArgumentNullException ("culture");
535 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
538 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
540 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
543 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
545 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
548 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
551 throw new ArgumentNullException ("culture");
553 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
554 throw new ArgumentOutOfRangeException ();
566 else if (strB == null) {
570 CompareOptions compopts;
573 compopts = CompareOptions.IgnoreCase;
575 compopts = CompareOptions.None;
577 // Need to cap the requested length to the
578 // length of the string, because
579 // CompareInfo.Compare will insist that length
580 // <= (string.Length - offset)
585 if (length > (strA.Length - indexA)) {
586 len1 = strA.Length - indexA;
589 if (length > (strB.Length - indexB)) {
590 len2 = strB.Length - indexB;
593 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
594 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
597 public static int Compare (string strA, string strB, StringComparison comparisonType)
599 switch (comparisonType) {
600 case StringComparison.CurrentCulture:
601 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
602 case StringComparison.CurrentCultureIgnoreCase:
603 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
604 case StringComparison.InvariantCulture:
605 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
606 case StringComparison.InvariantCultureIgnoreCase:
607 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
608 case StringComparison.Ordinal:
609 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
610 case StringComparison.OrdinalIgnoreCase:
611 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
613 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
614 throw new ArgumentException (msg, "comparisonType");
618 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
620 switch (comparisonType) {
621 case StringComparison.CurrentCulture:
622 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
623 case StringComparison.CurrentCultureIgnoreCase:
624 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
625 case StringComparison.InvariantCulture:
626 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
627 case StringComparison.InvariantCultureIgnoreCase:
628 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
629 case StringComparison.Ordinal:
630 return CompareOrdinal (strA, indexA, strB, indexB, length);
631 case StringComparison.OrdinalIgnoreCase:
632 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
634 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
635 throw new ArgumentException (msg, "comparisonType");
639 public static bool Equals (string a, string b, StringComparison comparisonType)
641 return String.Compare (a, b, comparisonType) == 0;
644 public bool Equals (string value, StringComparison comparisonType)
646 return String.Compare (value, this, comparisonType) == 0;
649 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
652 throw new ArgumentNullException ("culture");
654 return culture.CompareInfo.Compare (strA, strB, options);
657 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
660 throw new ArgumentNullException ("culture");
665 if (length > (strA.Length - indexA))
666 len1 = strA.Length - indexA;
668 if (length > (strB.Length - indexB))
669 len2 = strB.Length - indexB;
671 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
675 public int CompareTo (Object value)
680 if (!(value is String))
681 throw new ArgumentException ();
683 return String.Compare (this, (String) value);
686 public int CompareTo (String strB)
691 return Compare (this, strB);
694 public static int CompareOrdinal (String strA, String strB)
696 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
699 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
701 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
702 throw new ArgumentOutOfRangeException ();
704 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
707 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
709 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
710 throw new ArgumentOutOfRangeException ();
712 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
715 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
722 } else if (strB == null) {
725 int lengthA = Math.Min (lenA, strA.Length - indexA);
726 int lengthB = Math.Min (lenB, strB.Length - indexB);
728 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
731 fixed (char* aptr = strA, bptr = strB) {
732 char* ap = aptr + indexA;
733 char* end = ap + Math.Min (lengthA, lengthB);
734 char* bp = bptr + indexB;
741 return lengthA - lengthB;
745 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
747 // Same as above, but checks versus uppercase characters
753 } else if (strB == null) {
756 int lengthA = Math.Min (lenA, strA.Length - indexA);
757 int lengthB = Math.Min (lenB, strB.Length - indexB);
759 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
762 fixed (char* aptr = strA, bptr = strB) {
763 char* ap = aptr + indexA;
764 char* end = ap + Math.Min (lengthA, lengthB);
765 char* bp = bptr + indexB;
768 char c1 = Char.ToUpperInvariant (*ap);
769 char c2 = Char.ToUpperInvariant (*bp);
776 return lengthA - lengthB;
780 public bool EndsWith (String value)
783 throw new ArgumentNullException ("value");
785 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
793 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
796 throw new ArgumentNullException ("value");
798 culture = CultureInfo.CurrentCulture;
800 return culture.CompareInfo.IsSuffix (this, value,
801 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
804 // Following methods are culture-insensitive
805 public int IndexOfAny (char [] anyOf)
808 throw new ArgumentNullException ();
809 if (this.length == 0)
812 return IndexOfAnyUnchecked (anyOf, 0, this.length);
815 public int IndexOfAny (char [] anyOf, int startIndex)
818 throw new ArgumentNullException ();
819 if (startIndex < 0 || startIndex > this.length)
820 throw new ArgumentOutOfRangeException ();
822 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
825 public int IndexOfAny (char [] anyOf, int startIndex, int count)
828 throw new ArgumentNullException ();
829 if (startIndex < 0 || startIndex > this.length)
830 throw new ArgumentOutOfRangeException ();
831 if (count < 0 || startIndex > this.length - count)
832 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
834 return IndexOfAnyUnchecked (anyOf, startIndex, count);
837 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
839 if (anyOf.Length == 0)
842 if (anyOf.Length == 1)
843 return IndexOfUnchecked (anyOf[0], startIndex, count);
845 fixed (char* any = anyOf) {
849 char* end_any_ptr = any + anyOf.Length;
851 while (++any_ptr != end_any_ptr) {
852 if (*any_ptr > highest) {
857 if (*any_ptr < lowest)
861 fixed (char* start = &start_char) {
862 char* ptr = start + startIndex;
863 char* end_ptr = ptr + count;
865 while (ptr != end_ptr) {
866 if (*ptr > highest || *ptr < lowest) {
872 return (int)(ptr - start);
875 while (++any_ptr != end_any_ptr) {
876 if (*ptr == *any_ptr)
877 return (int)(ptr - start);
889 public int IndexOf (string value, StringComparison comparisonType)
891 return IndexOf (value, 0, this.Length, comparisonType);
894 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
896 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
899 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
901 switch (comparisonType) {
902 case StringComparison.CurrentCulture:
903 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
904 case StringComparison.CurrentCultureIgnoreCase:
905 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
906 case StringComparison.InvariantCulture:
907 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
908 case StringComparison.InvariantCultureIgnoreCase:
909 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
910 case StringComparison.Ordinal:
911 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
912 case StringComparison.OrdinalIgnoreCase:
913 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
915 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
916 throw new ArgumentException (msg, "comparisonType");
921 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
924 throw new ArgumentNullException ("value");
926 throw new ArgumentOutOfRangeException ("startIndex");
927 if (count < 0 || (this.length - startIndex) < count)
928 throw new ArgumentOutOfRangeException ("count");
930 if (options == CompareOptions.Ordinal)
931 return IndexOfOrdinalUnchecked (value, startIndex, count);
932 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
935 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
937 int valueLen = value.Length;
938 if (count < valueLen)
943 return IndexOfUnchecked (value[0], startIndex, count);
947 fixed (char* thisptr = this, valueptr = value) {
948 char* ap = thisptr + startIndex;
949 char* thisEnd = ap + count - valueLen + 1;
950 while (ap != thisEnd) {
951 if (*ap == *valueptr) {
952 for (int i = 1; i < valueLen; i++) {
953 if (ap[i] != valueptr[i])
956 return (int)(ap - thisptr);
965 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
967 int valueLen = value.Length;
968 if (count < valueLen)
974 fixed (char* thisptr = this, valueptr = value) {
975 char* ap = thisptr + startIndex;
976 char* thisEnd = ap + count - valueLen + 1;
977 while (ap != thisEnd) {
978 for (int i = 0; i < valueLen; i++) {
979 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
982 return (int)(ap - thisptr);
992 public int LastIndexOf (string value, StringComparison comparisonType)
994 if (this.Length == 0)
995 return value == String.Empty ? 0 : -1;
997 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1000 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1002 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1005 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1007 switch (comparisonType) {
1008 case StringComparison.CurrentCulture:
1009 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1010 case StringComparison.CurrentCultureIgnoreCase:
1011 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1012 case StringComparison.InvariantCulture:
1013 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1014 case StringComparison.InvariantCultureIgnoreCase:
1015 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1016 case StringComparison.Ordinal:
1017 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1018 case StringComparison.OrdinalIgnoreCase:
1019 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1021 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1022 throw new ArgumentException (msg, "comparisonType");
1027 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1030 throw new ArgumentNullException ("value");
1031 if (startIndex < 0 || startIndex > length)
1032 throw new ArgumentOutOfRangeException ("startIndex");
1033 if (count < 0 || (startIndex < count - 1))
1034 throw new ArgumentOutOfRangeException ("count");
1036 if (options == CompareOptions.Ordinal)
1037 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1038 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1041 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1043 int valueLen = value.Length;
1044 if (count < valueLen)
1047 if (valueLen <= 1) {
1049 return LastIndexOfUnchecked (value[0], startIndex, count);
1053 fixed (char* thisptr = this, valueptr = value) {
1054 char* ap = thisptr + startIndex - valueLen + 1;
1055 char* thisEnd = ap - count + valueLen - 1;
1056 while (ap != thisEnd) {
1057 if (*ap == *valueptr) {
1058 for (int i = 1; i < valueLen; i++) {
1059 if (ap[i] != valueptr[i])
1062 return (int)(ap - thisptr);
1071 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1073 int valueLen = value.Length;
1074 if (count < valueLen)
1080 fixed (char* thisptr = this, valueptr = value) {
1081 char* ap = thisptr + startIndex - valueLen + 1;
1082 char* thisEnd = ap - count + valueLen - 1;
1083 while (ap != thisEnd) {
1084 for (int i = 0; i < valueLen; i++) {
1085 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1088 return (int)(ap - thisptr);
1096 // Following methods are culture-insensitive
1097 public int IndexOf (char value)
1099 if (this.length == 0)
1102 return IndexOfUnchecked (value, 0, this.length);
1105 public int IndexOf (char value, int startIndex)
1108 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1109 if (startIndex > this.length)
1110 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1112 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1115 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1118 public int IndexOf (char value, int startIndex, int count)
1120 if (startIndex < 0 || startIndex > this.length)
1121 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1123 throw new ArgumentOutOfRangeException ("count", "< 0");
1124 if (startIndex > this.length - count)
1125 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1127 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1130 return IndexOfUnchecked (value, startIndex, count);
1133 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1135 // It helps JIT compiler to optimize comparison
1136 int value_32 = (int)value;
1138 fixed (char* start = &start_char) {
1139 char* ptr = start + startIndex;
1140 char* end_ptr = ptr + (count >> 3 << 3);
1142 while (ptr != end_ptr) {
1143 if (*ptr == value_32)
1144 return (int)(ptr - start);
1145 if (ptr[1] == value_32)
1146 return (int)(ptr - start + 1);
1147 if (ptr[2] == value_32)
1148 return (int)(ptr - start + 2);
1149 if (ptr[3] == value_32)
1150 return (int)(ptr - start + 3);
1151 if (ptr[4] == value_32)
1152 return (int)(ptr - start + 4);
1153 if (ptr[5] == value_32)
1154 return (int)(ptr - start + 5);
1155 if (ptr[6] == value_32)
1156 return (int)(ptr - start + 6);
1157 if (ptr[7] == value_32)
1158 return (int)(ptr - start + 7);
1163 end_ptr += count & 0x07;
1164 while (ptr != end_ptr) {
1165 if (*ptr == value_32)
1166 return (int)(ptr - start);
1174 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1178 int end = startIndex + count;
1179 char c = Char.ToUpperInvariant (value);
1180 fixed (char* s = &start_char) {
1181 for (int i = startIndex; i < end; i++)
1182 if (Char.ToUpperInvariant (s [i]) == c)
1188 // Following methods are culture-sensitive
1189 public int IndexOf (String value)
1192 throw new ArgumentNullException ("value");
1193 if (value.length == 0)
1195 if (this.length == 0)
1197 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1200 public int IndexOf (String value, int startIndex)
1202 return IndexOf (value, startIndex, this.length - startIndex);
1205 public int IndexOf (String value, int startIndex, int count)
1209 throw new ArgumentNullException ("value");
1211 throw new ArgumentNullException ("string2");
1213 if (startIndex < 0 || startIndex > this.length)
1214 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1215 if (count < 0 || startIndex > this.length - count)
1216 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1218 if (value.length == 0)
1221 if (startIndex == 0 && this.length == 0)
1227 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1230 // Following methods are culture-insensitive
1231 public int LastIndexOfAny (char [] anyOf)
1234 throw new ArgumentNullException ();
1236 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1239 public int LastIndexOfAny (char [] anyOf, int startIndex)
1242 throw new ArgumentNullException ();
1244 if (startIndex < 0 || startIndex >= this.length)
1245 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1247 if (this.length == 0)
1250 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1253 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1256 throw new ArgumentNullException ();
1258 if ((startIndex < 0) || (startIndex >= this.Length))
1259 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1260 if ((count < 0) || (count > this.Length))
1261 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1262 if (startIndex - count + 1 < 0)
1263 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1265 if (this.length == 0)
1268 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1271 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1273 if (anyOf.Length == 1)
1274 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1276 fixed (char* start = this, testStart = anyOf) {
1277 char* ptr = start + startIndex;
1278 char* ptrEnd = ptr - count;
1280 char* testEnd = testStart + anyOf.Length;
1282 while (ptr != ptrEnd) {
1284 while (test != testEnd) {
1286 return (int)(ptr - start);
1295 // Following methods are culture-insensitive
1296 public int LastIndexOf (char value)
1298 if (this.length == 0)
1301 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1304 public int LastIndexOf (char value, int startIndex)
1306 return LastIndexOf (value, startIndex, startIndex + 1);
1309 public int LastIndexOf (char value, int startIndex, int count)
1311 if (startIndex == 0 && this.length == 0)
1314 // >= for char (> for string)
1315 if ((startIndex < 0) || (startIndex >= this.Length))
1316 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1317 if ((count < 0) || (count > this.Length))
1318 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1319 if (startIndex - count + 1 < 0)
1320 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1322 return LastIndexOfUnchecked (value, startIndex, count);
1325 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1327 // It helps JIT compiler to optimize comparison
1328 int value_32 = (int)value;
1330 fixed (char* start = &start_char) {
1331 char* ptr = start + startIndex;
1332 char* end_ptr = ptr - (count >> 3 << 3);
1334 while (ptr != end_ptr) {
1335 if (*ptr == value_32)
1336 return (int)(ptr - start);
1337 if (ptr[-1] == value_32)
1338 return (int)(ptr - start) - 1;
1339 if (ptr[-2] == value_32)
1340 return (int)(ptr - start) - 2;
1341 if (ptr[-3] == value_32)
1342 return (int)(ptr - start) - 3;
1343 if (ptr[-4] == value_32)
1344 return (int)(ptr - start) - 4;
1345 if (ptr[-5] == value_32)
1346 return (int)(ptr - start) - 5;
1347 if (ptr[-6] == value_32)
1348 return (int)(ptr - start) - 6;
1349 if (ptr[-7] == value_32)
1350 return (int)(ptr - start) - 7;
1355 end_ptr -= count & 0x07;
1356 while (ptr != end_ptr) {
1357 if (*ptr == value_32)
1358 return (int)(ptr - start);
1366 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1370 int end = startIndex - count;
1371 char c = Char.ToUpperInvariant (value);
1372 fixed (char* s = &start_char) {
1373 for (int i = startIndex; i > end; i--)
1374 if (Char.ToUpperInvariant (s [i]) == c)
1380 // Following methods are culture-sensitive
1381 public int LastIndexOf (String value)
1383 if (this.length == 0)
1384 // This overload does additional checking
1385 return LastIndexOf (value, 0, 0);
1387 return LastIndexOf (value, this.length - 1, this.length);
1390 public int LastIndexOf (String value, int startIndex)
1392 int max = startIndex;
1393 if (max < this.Length)
1395 return LastIndexOf (value, startIndex, max);
1398 public int LastIndexOf (String value, int startIndex, int count)
1402 throw new ArgumentNullException ("value");
1404 throw new ArgumentNullException ("string2");
1407 // -1 > startIndex > for string (0 > startIndex >= for char)
1408 if ((startIndex < -1) || (startIndex > this.Length))
1409 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1410 if ((count < 0) || (count > this.Length))
1411 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1412 if (startIndex - count + 1 < 0)
1413 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1415 if (value.Length == 0)
1418 if (startIndex == 0 && this.length == 0)
1421 // This check is needed to match undocumented MS behaviour
1422 if (this.length == 0 && value.length > 0)
1428 if (startIndex == this.Length)
1430 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1434 public bool Contains (String value)
1436 return IndexOf (value) != -1;
1439 public static bool IsNullOrEmpty (String value)
1441 return (value == null) || (value.Length == 0);
1444 public string Normalize ()
1446 return Normalization.Normalize (this, 0);
1449 public string Normalize (NormalizationForm normalizationForm)
1451 switch (normalizationForm) {
1453 return Normalization.Normalize (this, 0);
1454 case NormalizationForm.FormD:
1455 return Normalization.Normalize (this, 1);
1456 case NormalizationForm.FormKC:
1457 return Normalization.Normalize (this, 2);
1458 case NormalizationForm.FormKD:
1459 return Normalization.Normalize (this, 3);
1463 public bool IsNormalized ()
1465 return Normalization.IsNormalized (this, 0);
1468 public bool IsNormalized (NormalizationForm normalizationForm)
1470 switch (normalizationForm) {
1472 return Normalization.IsNormalized (this, 0);
1473 case NormalizationForm.FormD:
1474 return Normalization.IsNormalized (this, 1);
1475 case NormalizationForm.FormKC:
1476 return Normalization.IsNormalized (this, 2);
1477 case NormalizationForm.FormKD:
1478 return Normalization.IsNormalized (this, 3);
1482 public string Remove (int startIndex)
1485 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1486 if (startIndex >= this.length)
1487 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1489 return Remove (startIndex, this.length - startIndex);
1493 public String PadLeft (int totalWidth)
1495 return PadLeft (totalWidth, ' ');
1498 public unsafe String PadLeft (int totalWidth, char paddingChar)
1500 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1503 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1505 if (totalWidth < this.length)
1508 String tmp = InternalAllocateStr (totalWidth);
1510 fixed (char* dest = tmp, src = this) {
1511 char* padPos = dest;
1512 char* padTo = dest + (totalWidth - length);
1513 while (padPos != padTo)
1514 *padPos++ = paddingChar;
1516 CharCopy (padTo, src, length);
1521 public String PadRight (int totalWidth)
1523 return PadRight (totalWidth, ' ');
1526 public unsafe String PadRight (int totalWidth, char paddingChar)
1528 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1531 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1533 if (totalWidth < this.length)
1536 String tmp = InternalAllocateStr (totalWidth);
1538 fixed (char* dest = tmp, src = this) {
1539 CharCopy (dest, src, length);
1541 char* padPos = dest + length;
1542 char* padTo = dest + totalWidth;
1543 while (padPos != padTo)
1544 *padPos++ = paddingChar;
1549 public bool StartsWith (String value)
1552 throw new ArgumentNullException ("value");
1554 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1558 [ComVisible (false)]
1559 public bool StartsWith (string value, StringComparison comparisonType)
1562 throw new ArgumentNullException ("value");
1564 switch (comparisonType) {
1565 case StringComparison.CurrentCulture:
1566 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1567 case StringComparison.CurrentCultureIgnoreCase:
1568 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1569 case StringComparison.InvariantCulture:
1570 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1571 case StringComparison.InvariantCultureIgnoreCase:
1572 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1573 case StringComparison.Ordinal:
1574 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1575 case StringComparison.OrdinalIgnoreCase:
1576 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1578 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1579 throw new ArgumentException (msg, "comparisonType");
1583 [ComVisible (false)]
1584 public bool EndsWith (string value, StringComparison comparisonType)
1587 throw new ArgumentNullException ("value");
1589 switch (comparisonType) {
1590 case StringComparison.CurrentCulture:
1591 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1592 case StringComparison.CurrentCultureIgnoreCase:
1593 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1594 case StringComparison.InvariantCulture:
1595 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1596 case StringComparison.InvariantCultureIgnoreCase:
1597 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1598 case StringComparison.Ordinal:
1599 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1600 case StringComparison.OrdinalIgnoreCase:
1601 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1603 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1604 throw new ArgumentException (msg, "comparisonType");
1614 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1616 if (culture == null)
1617 culture = CultureInfo.CurrentCulture;
1619 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1622 // Following method is culture-insensitive
1623 public unsafe String Replace (char oldChar, char newChar)
1625 if (this.length == 0 || oldChar == newChar)
1628 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1629 if (start_pos == -1)
1635 string tmp = InternalAllocateStr (length);
1636 fixed (char* dest = tmp, src = &start_char) {
1638 CharCopy (dest, src, start_pos);
1640 char* end_ptr = dest + length;
1641 char* dest_ptr = dest + start_pos;
1642 char* src_ptr = src + start_pos;
1644 while (dest_ptr != end_ptr) {
1645 if (*src_ptr == oldChar)
1646 *dest_ptr = newChar;
1648 *dest_ptr = *src_ptr;
1657 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1658 public String Replace (String oldValue, String newValue)
1660 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1661 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1663 if (oldValue == null)
1664 throw new ArgumentNullException ("oldValue");
1666 if (oldValue.Length == 0)
1667 throw new ArgumentException ("oldValue is the empty string.");
1669 if (this.Length == 0)
1672 if (newValue == null)
1673 newValue = String.Empty;
1675 return ReplaceUnchecked (oldValue, newValue);
1678 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1680 if (oldValue.length > length)
1682 if (oldValue.length == 1 && newValue.length == 1) {
1683 return Replace (oldValue[0], newValue[0]);
1684 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1685 // because the length of the result would be this.length and length calculation unneccesary
1688 const int maxValue = 200; // Allocate 800 byte maximum
1689 int* dat = stackalloc int[maxValue];
1690 fixed (char* source = this, replace = newValue) {
1691 int i = 0, count = 0;
1692 while (i < length) {
1693 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1697 if (count < maxValue)
1698 dat[count++] = found;
1700 return ReplaceFallback (oldValue, newValue, maxValue);
1702 i = found + oldValue.length;
1706 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1707 String tmp = InternalAllocateStr (nlen);
1709 int curPos = 0, lastReadPos = 0;
1710 fixed (char* dest = tmp) {
1711 for (int j = 0; j < count; j++) {
1712 int precopy = dat[j] - lastReadPos;
1713 CharCopy (dest + curPos, source + lastReadPos, precopy);
1715 lastReadPos = dat[j] + oldValue.length;
1716 CharCopy (dest + curPos, replace, newValue.length);
1717 curPos += newValue.length;
1719 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1725 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1727 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1728 StringBuilder sb = new StringBuilder (lengthEstimate);
1729 for (int i = 0; i < length;) {
1730 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1732 sb.Append (SubstringUnchecked (i, length - i));
1735 sb.Append (SubstringUnchecked (i, found - i));
1736 sb.Append (newValue);
1737 i = found + oldValue.Length;
1739 return sb.ToString ();
1743 public unsafe String Remove (int startIndex, int count)
1746 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1748 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1749 if (startIndex > this.length - count)
1750 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1752 String tmp = InternalAllocateStr (this.length - count);
1754 fixed (char *dest = tmp, src = this) {
1756 CharCopy (dst, src, startIndex);
1757 int skip = startIndex + count;
1759 CharCopy (dst, src + skip, length - skip);
1764 public String ToLower ()
1766 return ToLower (CultureInfo.CurrentCulture);
1769 public String ToLower (CultureInfo culture)
1771 if (culture == null)
1772 throw new ArgumentNullException ("culture");
1774 if (culture.LCID == 0x007F) // Invariant
1775 return ToLowerInvariant ();
1777 return culture.TextInfo.ToLower (this);
1781 public unsafe String ToLowerInvariant ()
1783 internal unsafe String ToLowerInvariant ()
1786 string tmp = InternalAllocateStr (length);
1787 fixed (char* source = &start_char, dest = tmp) {
1789 char* destPtr = (char*)dest;
1790 char* sourcePtr = (char*)source;
1792 for (int n = 0; n < length; n++) {
1793 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1801 public String ToUpper ()
1803 return ToUpper (CultureInfo.CurrentCulture);
1806 public String ToUpper (CultureInfo culture)
1808 if (culture == null)
1809 throw new ArgumentNullException ("culture");
1811 if (culture.LCID == 0x007F) // Invariant
1812 return ToUpperInvariant ();
1814 return culture.TextInfo.ToUpper (this);
1818 public unsafe String ToUpperInvariant ()
1820 internal unsafe String ToUpperInvariant ()
1823 string tmp = InternalAllocateStr (length);
1824 fixed (char* source = &start_char, dest = tmp) {
1826 char* destPtr = (char*)dest;
1827 char* sourcePtr = (char*)source;
1829 for (int n = 0; n < length; n++) {
1830 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1838 public override String ToString ()
1843 public String ToString (IFormatProvider provider)
1848 public static String Format (String format, Object arg0)
1850 return Format (null, format, new Object[] {arg0});
1853 public static String Format (String format, Object arg0, Object arg1)
1855 return Format (null, format, new Object[] {arg0, arg1});
1858 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1860 return Format (null, format, new Object[] {arg0, arg1, arg2});
1863 public static string Format (string format, params object[] args)
1865 return Format (null, format, args);
1868 public static string Format (IFormatProvider provider, string format, params object[] args)
1870 StringBuilder b = FormatHelper (null, provider, format, args);
1871 return b.ToString ();
1874 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1877 throw new ArgumentNullException ("format");
1879 throw new ArgumentNullException ("args");
1881 if (result == null) {
1882 /* Try to approximate the size of result to avoid reallocations */
1886 for (i = 0; i < args.Length; ++i) {
1887 string s = args [i] as string;
1893 if (i == args.Length)
1894 result = new StringBuilder (len + format.length);
1896 result = new StringBuilder ();
1901 while (ptr < format.length) {
1902 char c = format[ptr ++];
1905 result.Append (format, start, ptr - start - 1);
1907 // check for escaped open bracket
1909 if (format[ptr] == '{') {
1920 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1921 if (n >= args.Length)
1922 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1926 object arg = args[n];
1929 ICustomFormatter formatter = null;
1930 if (provider != null)
1931 formatter = provider.GetFormat (typeof (ICustomFormatter))
1932 as ICustomFormatter;
1935 else if (formatter != null)
1936 str = formatter.Format (arg_format, arg, provider);
1937 else if (arg is IFormattable)
1938 str = ((IFormattable)arg).ToString (arg_format, provider);
1940 str = arg.ToString ();
1942 // pad formatted string and append to result
1944 if (width > str.length) {
1945 const char padchar = ' ';
1946 int padlen = width - str.length;
1949 result.Append (str);
1950 result.Append (padchar, padlen);
1953 result.Append (padchar, padlen);
1954 result.Append (str);
1958 result.Append (str);
1962 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1963 result.Append (format, start, ptr - start - 1);
1966 else if (c == '}') {
1967 throw new FormatException ("Input string was not in a correct format.");
1971 if (start < format.length)
1972 result.Append (format, start, format.Length - start);
1977 public unsafe static String Copy (String str)
1980 throw new ArgumentNullException ("str");
1982 int length = str.length;
1984 String tmp = InternalAllocateStr (length);
1986 fixed (char *dest = tmp, src = str) {
1987 CharCopy (dest, src, length);
1993 public static String Concat (Object arg0)
1996 return String.Empty;
1998 return arg0.ToString ();
2001 public unsafe static String Concat (Object arg0, Object arg1)
2005 s1 = (arg0 != null) ? arg0.ToString () : null;
2006 s2 = (arg1 != null) ? arg1.ToString () : null;
2010 return String.Empty;
2013 } else if (s2 == null)
2016 String tmp = InternalAllocateStr (s1.Length + s2.Length);
2017 if (s1.Length != 0) {
2018 fixed (char *dest = tmp, src = s1) {
2019 CharCopy (dest, src, s1.length);
2022 if (s2.Length != 0) {
2023 fixed (char *dest = tmp, src = s2) {
2024 CharCopy (dest + s1.Length, src, s2.length);
2031 public static String Concat (Object arg0, Object arg1, Object arg2)
2037 s1 = arg0.ToString ();
2042 s2 = arg1.ToString ();
2047 s3 = arg2.ToString ();
2049 return Concat (s1, s2, s3);
2052 #if ! BOOTSTRAP_WITH_OLDLIB
2053 [CLSCompliant(false)]
2054 public static String Concat (Object arg0, Object arg1, Object arg2,
2055 Object arg3, __arglist)
2057 string s1, s2, s3, s4;
2062 s1 = arg0.ToString ();
2067 s2 = arg1.ToString ();
2072 s3 = arg2.ToString ();
2074 ArgIterator iter = new ArgIterator (__arglist);
2075 int argCount = iter.GetRemainingCount();
2077 StringBuilder sb = new StringBuilder ();
2079 sb.Append (arg3.ToString ());
2081 for (int i = 0; i < argCount; i++) {
2082 TypedReference typedRef = iter.GetNextArg ();
2083 sb.Append (TypedReference.ToObject (typedRef));
2086 s4 = sb.ToString ();
2088 return Concat (s1, s2, s3, s4);
2092 public unsafe static String Concat (String str0, String str1)
2094 if (str0 == null || str0.Length == 0) {
2095 if (str1 == null || str1.Length == 0)
2096 return String.Empty;
2100 if (str1 == null || str1.Length == 0)
2103 String tmp = InternalAllocateStr (str0.length + str1.length);
2105 fixed (char *dest = tmp, src = str0)
2106 CharCopy (dest, src, str0.length);
2107 fixed (char *dest = tmp, src = str1)
2108 CharCopy (dest + str0.Length, src, str1.length);
2113 public unsafe static String Concat (String str0, String str1, String str2)
2115 if (str0 == null || str0.Length == 0){
2116 if (str1 == null || str1.Length == 0){
2117 if (str2 == null || str2.Length == 0)
2118 return String.Empty;
2121 if (str2 == null || str2.Length == 0)
2124 str0 = String.Empty;
2126 if (str1 == null || str1.Length == 0){
2127 if (str2 == null || str2.Length == 0)
2130 str1 = String.Empty;
2132 if (str2 == null || str2.Length == 0)
2133 str2 = String.Empty;
2137 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2139 if (str0.Length != 0) {
2140 fixed (char *dest = tmp, src = str0) {
2141 CharCopy (dest, src, str0.length);
2144 if (str1.Length != 0) {
2145 fixed (char *dest = tmp, src = str1) {
2146 CharCopy (dest + str0.Length, src, str1.length);
2149 if (str2.Length != 0) {
2150 fixed (char *dest = tmp, src = str2) {
2151 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2158 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2160 if (str0 == null && str1 == null && str2 == null && str3 == null)
2161 return String.Empty;
2164 str0 = String.Empty;
2166 str1 = String.Empty;
2168 str2 = String.Empty;
2170 str3 = String.Empty;
2172 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2174 if (str0.Length != 0) {
2175 fixed (char *dest = tmp, src = str0) {
2176 CharCopy (dest, src, str0.length);
2179 if (str1.Length != 0) {
2180 fixed (char *dest = tmp, src = str1) {
2181 CharCopy (dest + str0.Length, src, str1.length);
2184 if (str2.Length != 0) {
2185 fixed (char *dest = tmp, src = str2) {
2186 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2189 if (str3.Length != 0) {
2190 fixed (char *dest = tmp, src = str3) {
2191 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2198 public static String Concat (params Object[] args)
2201 throw new ArgumentNullException ("args");
2203 int argLen = args.Length;
2205 return String.Empty;
2207 string [] strings = new string [argLen];
2209 for (int i = 0; i < argLen; i++) {
2210 if (args[i] != null) {
2211 strings[i] = args[i].ToString ();
2212 len += strings[i].length;
2216 return String.Empty;
2218 return ConcatInternal (strings, len);
2221 public static String Concat (params String[] values)
2224 throw new ArgumentNullException ("values");
2227 for (int i = 0; i < values.Length; i++) {
2228 String s = values[i];
2233 return String.Empty;
2235 return ConcatInternal (values, len);
2238 private static unsafe String ConcatInternal (String[] values, int length)
2240 String tmp = InternalAllocateStr (length);
2242 fixed (char* dest = tmp) {
2244 for (int i = 0; i < values.Length; i++) {
2245 String source = values[i];
2246 if (source != null) {
2247 fixed (char* src = source) {
2248 CharCopy (dest + pos, src, source.length);
2250 pos += source.Length;
2257 public unsafe String Insert (int startIndex, String value)
2260 throw new ArgumentNullException ("value");
2262 if (startIndex < 0 || startIndex > this.length)
2263 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2265 if (value.Length == 0)
2267 if (this.Length == 0)
2269 String tmp = InternalAllocateStr (this.length + value.length);
2271 fixed (char *dest = tmp, src = this, val = value) {
2273 CharCopy (dst, src, startIndex);
2275 CharCopy (dst, val, value.length);
2276 dst += value.length;
2277 CharCopy (dst, src + startIndex, length - startIndex);
2282 public static string Intern (string str)
2285 throw new ArgumentNullException ("str");
2287 return InternalIntern (str);
2290 public static string IsInterned (string str)
2293 throw new ArgumentNullException ("str");
2295 return InternalIsInterned (str);
2298 public static string Join (string separator, string [] value)
2301 throw new ArgumentNullException ("value");
2302 if (separator == null)
2303 separator = String.Empty;
2305 return JoinUnchecked (separator, value, 0, value.Length);
2308 public static string Join (string separator, string[] value, int startIndex, int count)
2311 throw new ArgumentNullException ("value");
2313 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2315 throw new ArgumentOutOfRangeException ("count", "< 0");
2316 if (startIndex > value.Length - count)
2317 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2319 if (startIndex == value.Length)
2320 return String.Empty;
2321 if (separator == null)
2322 separator = String.Empty;
2324 return JoinUnchecked (separator, value, startIndex, count);
2327 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2329 // Unchecked parameters
2330 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2331 // separator and value must not be null
2334 int maxIndex = startIndex + count;
2335 // Precount the number of characters that the resulting string will have
2336 for (int i = startIndex; i < maxIndex; i++) {
2337 String s = value[i];
2341 length += separator.length * (count - 1);
2343 return String.Empty;
2345 String tmp = InternalAllocateStr (length);
2348 fixed (char* dest = tmp, sepsrc = separator) {
2349 // Copy each string from value except the last one and add a separator for each
2351 for (int i = startIndex; i < maxIndex; i++) {
2352 String source = value[i];
2353 if (source != null) {
2354 if (source.Length > 0) {
2355 fixed (char* src = source)
2356 CharCopy (dest + pos, src, source.Length);
2357 pos += source.Length;
2360 if (separator.Length > 0) {
2361 CharCopy (dest + pos, sepsrc, separator.Length);
2362 pos += separator.Length;
2365 // Append last string that does not get an additional separator
2366 String sourceLast = value[maxIndex];
2367 if (sourceLast != null) {
2368 if (sourceLast.Length > 0) {
2369 fixed (char* src = sourceLast)
2370 CharCopy (dest + pos, src, sourceLast.Length);
2377 bool IConvertible.ToBoolean (IFormatProvider provider)
2379 return Convert.ToBoolean (this, provider);
2382 byte IConvertible.ToByte (IFormatProvider provider)
2384 return Convert.ToByte (this, provider);
2387 char IConvertible.ToChar (IFormatProvider provider)
2389 return Convert.ToChar (this, provider);
2392 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2394 return Convert.ToDateTime (this, provider);
2397 decimal IConvertible.ToDecimal (IFormatProvider provider)
2399 return Convert.ToDecimal (this, provider);
2402 double IConvertible.ToDouble (IFormatProvider provider)
2404 return Convert.ToDouble (this, provider);
2407 short IConvertible.ToInt16 (IFormatProvider provider)
2409 return Convert.ToInt16 (this, provider);
2412 int IConvertible.ToInt32 (IFormatProvider provider)
2414 return Convert.ToInt32 (this, provider);
2417 long IConvertible.ToInt64 (IFormatProvider provider)
2419 return Convert.ToInt64 (this, provider);
2423 #pragma warning disable 3019
2424 [CLSCompliant (false)]
2426 sbyte IConvertible.ToSByte (IFormatProvider provider)
2428 return Convert.ToSByte (this, provider);
2431 #pragma warning restore 3019
2434 float IConvertible.ToSingle (IFormatProvider provider)
2436 return Convert.ToSingle (this, provider);
2439 object IConvertible.ToType (Type type, IFormatProvider provider)
2441 return Convert.ToType (this, type, provider, false);
2445 #pragma warning disable 3019
2446 [CLSCompliant (false)]
2448 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2450 return Convert.ToUInt16 (this, provider);
2453 #pragma warning restore 3019
2457 #pragma warning disable 3019
2458 [CLSCompliant (false)]
2460 uint IConvertible.ToUInt32 (IFormatProvider provider)
2462 return Convert.ToUInt32 (this, provider);
2465 #pragma warning restore 3019
2469 #pragma warning disable 3019
2470 [CLSCompliant (false)]
2472 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2474 return Convert.ToUInt64 (this, provider);
2477 #pragma warning restore 3019
2486 public CharEnumerator GetEnumerator ()
2488 return new CharEnumerator (this);
2492 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2494 return new CharEnumerator (this);
2498 IEnumerator IEnumerable.GetEnumerator ()
2500 return new CharEnumerator (this);
2503 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2504 out bool left_align, out string format)
2506 // parses format specifier of form:
2512 // N = argument number (non-negative integer)
2514 n = ParseDecimal (str, ref ptr);
2516 throw new FormatException ("Input string was not in a correct format.");
2518 // M = width (non-negative integer)
2520 if (str[ptr] == ',') {
2521 // White space between ',' and number or sign.
2523 while (Char.IsWhiteSpace (str [ptr]))
2527 format = str.Substring (start, ptr - start);
2529 left_align = (str [ptr] == '-');
2533 width = ParseDecimal (str, ref ptr);
2535 throw new FormatException ("Input string was not in a correct format.");
2540 format = String.Empty;
2543 // F = argument format (string)
2545 if (str[ptr] == ':') {
2547 while (str[ptr] != '}')
2550 format += str.Substring (start, ptr - start);
2555 if (str[ptr ++] != '}')
2556 throw new FormatException ("Input string was not in a correct format.");
2558 catch (IndexOutOfRangeException) {
2559 throw new FormatException ("Input string was not in a correct format.");
2563 private static int ParseDecimal (string str, ref int ptr)
2569 if (c < '0' || '9' < c)
2572 n = n * 10 + c - '0';
2583 internal unsafe void InternalSetChar (int idx, char val)
2585 if ((uint) idx >= (uint) Length)
2586 throw new ArgumentOutOfRangeException ("idx");
2588 fixed (char * pStr = &start_char)
2594 internal unsafe void InternalSetLength (int newLength)
2596 if (newLength > length)
2597 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2599 // zero terminate, we can pass string objects directly via pinvoke
2600 // we also zero the rest of the string, since the new GC needs to be
2601 // able to handle the changing size (it will skip the 0 bytes).
2602 fixed (char * pStr = &start_char) {
2603 char *p = pStr + newLength;
2604 char *end = pStr + length;
2614 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2616 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2617 public unsafe override int GetHashCode ()
2619 fixed (char * c = this) {
2621 char * end = cc + length - 1;
2623 for (;cc < end; cc += 2) {
2624 h = (h << 5) - h + *cc;
2625 h = (h << 5) - h + cc [1];
2629 h = (h << 5) - h + *cc;
2634 internal unsafe int GetCaseInsensitiveHashCode ()
2636 fixed (char * c = this) {
2638 char * end = cc + length - 1;
2640 for (;cc < end; cc += 2) {
2641 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2642 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2646 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2651 // Certain constructors are redirected to CreateString methods with
2652 // matching argument list. The this pointer should not be used.
2653 #pragma warning disable 169
2654 private unsafe String CreateString (sbyte* value)
2657 return String.Empty;
2659 byte* bytes = (byte*) value;
2663 while (bytes++ [0] != 0)
2665 } catch (NullReferenceException) {
2666 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2668 } catch (AccessViolationException) {
2669 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2673 return CreateString (value, 0, length, null);
2676 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2678 return CreateString (value, startIndex, length, null);
2681 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2684 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2686 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2687 if (value + startIndex < value)
2688 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2690 bool isDefaultEncoding;
2692 if (isDefaultEncoding = (enc == null)) {
2695 throw new ArgumentNullException ("value");
2698 if (value == null || length == 0)
2700 return String.Empty;
2702 enc = Encoding.Default;
2705 byte [] bytes = new byte [length];
2708 fixed (byte* bytePtr = bytes)
2710 memcpy (bytePtr, (byte*) (value + startIndex), length);
2711 } catch (NullReferenceException) {
2713 if (!isDefaultEncoding)
2717 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2719 } catch (AccessViolationException) {
2720 if (!isDefaultEncoding)
2723 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2727 // GetString () is called even when length == 0
2728 return enc.GetString (bytes);
2731 unsafe string CreateString (char *value)
2734 return string.Empty;
2741 string result = InternalAllocateStr (i);
2744 fixed (char *dest = result) {
2745 CharCopy (dest, value, i);
2751 unsafe string CreateString (char *value, int startIndex, int length)
2754 return string.Empty;
2756 throw new ArgumentNullException ("value");
2758 throw new ArgumentOutOfRangeException ("startIndex");
2760 throw new ArgumentOutOfRangeException ("length");
2762 string result = InternalAllocateStr (length);
2764 fixed (char *dest = result) {
2765 CharCopy (dest, value + startIndex, length);
2770 unsafe string CreateString (char [] val, int startIndex, int length)
2773 throw new ArgumentNullException ("value");
2775 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2777 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2778 if (startIndex > val.Length - length)
2779 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2781 return string.Empty;
2783 string result = InternalAllocateStr (length);
2785 fixed (char *dest = result, src = val) {
2786 CharCopy (dest, src + startIndex, length);
2791 unsafe string CreateString (char [] val)
2794 return string.Empty;
2795 if (val.Length == 0)
2796 return string.Empty;
2797 string result = InternalAllocateStr (val.Length);
2799 fixed (char *dest = result, src = val) {
2800 CharCopy (dest, src, val.Length);
2805 unsafe string CreateString (char c, int count)
2808 throw new ArgumentOutOfRangeException ("count");
2810 return string.Empty;
2811 string result = InternalAllocateStr (count);
2812 fixed (char *dest = result) {
2814 char *end = p + count;
2822 #pragma warning restore 169
2824 /* helpers used by the runtime as well as above or eslewhere in corlib */
2825 internal static unsafe void memset (byte *dest, int val, int len)
2836 val = val | (val << 8);
2837 val = val | (val << 16);
2840 int rest = (int)dest & 3;
2848 } while (rest != 0);
2851 ((int*)dest) [0] = val;
2852 ((int*)dest) [1] = val;
2853 ((int*)dest) [2] = val;
2854 ((int*)dest) [3] = val;
2859 ((int*)dest) [0] = val;
2871 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2872 /*while (size >= 32) {
2873 // using long is better than int and slower than double
2874 // FIXME: enable this only on correct alignment or on platforms
2875 // that can tolerate unaligned reads/writes of doubles
2876 ((double*)dest) [0] = ((double*)src) [0];
2877 ((double*)dest) [1] = ((double*)src) [1];
2878 ((double*)dest) [2] = ((double*)src) [2];
2879 ((double*)dest) [3] = ((double*)src) [3];
2884 while (size >= 16) {
2885 ((int*)dest) [0] = ((int*)src) [0];
2886 ((int*)dest) [1] = ((int*)src) [1];
2887 ((int*)dest) [2] = ((int*)src) [2];
2888 ((int*)dest) [3] = ((int*)src) [3];
2894 ((int*)dest) [0] = ((int*)src) [0];
2900 ((byte*)dest) [0] = ((byte*)src) [0];
2906 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2908 ((short*)dest) [0] = ((short*)src) [0];
2909 ((short*)dest) [1] = ((short*)src) [1];
2910 ((short*)dest) [2] = ((short*)src) [2];
2911 ((short*)dest) [3] = ((short*)src) [3];
2917 ((short*)dest) [0] = ((short*)src) [0];
2923 ((byte*)dest) [0] = ((byte*)src) [0];
2925 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2927 ((byte*)dest) [0] = ((byte*)src) [0];
2928 ((byte*)dest) [1] = ((byte*)src) [1];
2929 ((byte*)dest) [2] = ((byte*)src) [2];
2930 ((byte*)dest) [3] = ((byte*)src) [3];
2931 ((byte*)dest) [4] = ((byte*)src) [4];
2932 ((byte*)dest) [5] = ((byte*)src) [5];
2933 ((byte*)dest) [6] = ((byte*)src) [6];
2934 ((byte*)dest) [7] = ((byte*)src) [7];
2940 ((byte*)dest) [0] = ((byte*)src) [0];
2941 ((byte*)dest) [1] = ((byte*)src) [1];
2947 ((byte*)dest) [0] = ((byte*)src) [0];
2950 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2951 // FIXME: if pointers are not aligned, try to align them
2952 // so a faster routine can be used. Handle the case where
2953 // the pointers can't be reduced to have the same alignment
2954 // (just ignore the issue on x86?)
2955 if ((((int)dest | (int)src) & 3) != 0) {
2956 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2962 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2963 ((short*)dest) [0] = ((short*)src) [0];
2968 if ((((int)dest | (int)src) & 1) != 0) {
2969 memcpy1 (dest, src, size);
2972 if ((((int)dest | (int)src) & 2) != 0) {
2973 memcpy2 (dest, src, size);
2977 memcpy4 (dest, src, size);
2980 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2981 // Same rules as for memcpy, but with the premise that
2982 // chars can only be aligned to even addresses if their
2983 // enclosing types are correctly aligned
2984 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2985 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2986 ((short*)dest) [0] = ((short*)src) [0];
2991 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2992 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2996 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2999 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3003 for (int i = count; i > 0; i--) {
3010 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3012 fixed (char* dest = target, src = source)
3013 CharCopy (dest + targetIndex, src + sourceIndex, count);
3016 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3018 fixed (char* dest = target, src = source)
3019 CharCopy (dest + targetIndex, src + sourceIndex, count);
3022 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3023 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3025 fixed (char* dest = target, src = source)
3026 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3029 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3030 unsafe public extern String (char *value);
3032 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3033 unsafe public extern String (char *value, int startIndex, int length);
3035 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3036 unsafe public extern String (sbyte *value);
3038 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3039 unsafe public extern String (sbyte *value, int startIndex, int length);
3041 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3042 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3044 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3045 public extern String (char [] value, int startIndex, int length);
3047 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3048 public extern String (char [] value);
3050 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3051 public extern String (char c, int count);
3053 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3054 // private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
3056 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3057 // private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
3059 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3060 // private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3062 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3063 private extern String[] InternalSplit (char[] separator, int count, int options);
3065 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3066 // private extern String InternalTrim (char[] chars, int typ);
3068 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3069 // private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3071 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3072 // private extern String InternalPad (int width, char chr, bool right);
3074 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3075 internal extern static String InternalAllocateStr (int length);
3077 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3078 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3080 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3081 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3083 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3084 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3086 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3087 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3089 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3090 private extern static string InternalIntern (string str);
3092 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3093 private extern static string InternalIsInterned (string str);