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 = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
388 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
390 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
391 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
392 (char) 0x3000, (char) 0xFEFF };
394 public String Trim ()
398 int start = FindNotWhiteSpace (0, length, 1);
403 int end = FindNotWhiteSpace (length - 1, start, -1);
405 int newLength = end - start + 1;
406 if (newLength == length)
409 return SubstringUnchecked (start, newLength);
412 public String Trim (params char[] trimChars)
414 if (trimChars == null || trimChars.Length == 0)
419 int start = FindNotInTable (0, length, 1, trimChars);
424 int end = FindNotInTable (length - 1, start, -1, trimChars);
426 int newLength = end - start + 1;
427 if (newLength == length)
430 return SubstringUnchecked (start, newLength);
433 public String TrimStart (params char[] trimChars)
438 if (trimChars == null || trimChars.Length == 0)
439 start = FindNotWhiteSpace (0, length, 1);
441 start = FindNotInTable (0, length, 1, trimChars);
446 return SubstringUnchecked (start, length - start);
449 public String TrimEnd (params char[] trimChars)
454 if (trimChars == null || trimChars.Length == 0)
455 end = FindNotWhiteSpace (length - 1, -1, -1);
457 end = FindNotInTable (length - 1, -1, -1, trimChars);
463 return SubstringUnchecked (0, end);
466 private int FindNotWhiteSpace (int pos, int target, int change)
468 while (pos != target) {
472 if (c < 0x9 || c > 0xD)
477 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
479 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
481 if (c < 0x2000 || c > 0x200B)
490 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
492 fixed (char* tablePtr = table, thisPtr = this) {
493 while (pos != target) {
494 char c = thisPtr[pos];
496 while (x < table.Length) {
497 if (c == tablePtr[x])
501 if (x == table.Length)
509 public static int Compare (String strA, String strB)
511 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
514 public static int Compare (String strA, String strB, bool ignoreCase)
516 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
519 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
522 throw new ArgumentNullException ("culture");
524 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
527 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
529 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
532 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
534 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
537 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
540 throw new ArgumentNullException ("culture");
542 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
543 throw new ArgumentOutOfRangeException ();
555 else if (strB == null) {
559 CompareOptions compopts;
562 compopts = CompareOptions.IgnoreCase;
564 compopts = CompareOptions.None;
566 // Need to cap the requested length to the
567 // length of the string, because
568 // CompareInfo.Compare will insist that length
569 // <= (string.Length - offset)
574 if (length > (strA.Length - indexA)) {
575 len1 = strA.Length - indexA;
578 if (length > (strB.Length - indexB)) {
579 len2 = strB.Length - indexB;
582 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
583 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
586 public static int Compare (string strA, string strB, StringComparison comparisonType)
588 switch (comparisonType) {
589 case StringComparison.CurrentCulture:
590 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
591 case StringComparison.CurrentCultureIgnoreCase:
592 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
593 case StringComparison.InvariantCulture:
594 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
595 case StringComparison.InvariantCultureIgnoreCase:
596 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
597 case StringComparison.Ordinal:
598 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
599 case StringComparison.OrdinalIgnoreCase:
600 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
602 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
603 throw new ArgumentException (msg, "comparisonType");
607 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
609 switch (comparisonType) {
610 case StringComparison.CurrentCulture:
611 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
612 case StringComparison.CurrentCultureIgnoreCase:
613 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
614 case StringComparison.InvariantCulture:
615 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
616 case StringComparison.InvariantCultureIgnoreCase:
617 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
618 case StringComparison.Ordinal:
619 return CompareOrdinal (strA, indexA, strB, indexB, length);
620 case StringComparison.OrdinalIgnoreCase:
621 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
623 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
624 throw new ArgumentException (msg, "comparisonType");
628 public static bool Equals (string a, string b, StringComparison comparisonType)
630 return String.Compare (a, b, comparisonType) == 0;
633 public bool Equals (string value, StringComparison comparisonType)
635 return String.Compare (value, this, comparisonType) == 0;
640 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
643 throw new ArgumentNullException ("culture");
645 return culture.CompareInfo.Compare (strA, strB, options);
648 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
651 throw new ArgumentNullException ("culture");
656 if (length > (strA.Length - indexA))
657 len1 = strA.Length - indexA;
659 if (length > (strB.Length - indexB))
660 len2 = strB.Length - indexB;
662 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
666 public int CompareTo (Object value)
671 if (!(value is String))
672 throw new ArgumentException ();
674 return String.Compare (this, (String) value);
677 public int CompareTo (String strB)
682 return Compare (this, strB);
685 public static int CompareOrdinal (String strA, String strB)
687 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
690 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
692 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
693 throw new ArgumentOutOfRangeException ();
695 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
698 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
700 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
701 throw new ArgumentOutOfRangeException ();
703 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
706 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
713 } else if (strB == null) {
716 int lengthA = Math.Min (lenA, strA.Length - indexA);
717 int lengthB = Math.Min (lenB, strB.Length - indexB);
719 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
722 fixed (char* aptr = strA, bptr = strB) {
723 char* ap = aptr + indexA;
724 char* end = ap + Math.Min (lengthA, lengthB);
725 char* bp = bptr + indexB;
732 return lengthA - lengthB;
736 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
738 // Same as above, but checks versus uppercase characters
744 } else if (strB == null) {
747 int lengthA = Math.Min (lenA, strA.Length - indexA);
748 int lengthB = Math.Min (lenB, strB.Length - indexB);
750 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
753 fixed (char* aptr = strA, bptr = strB) {
754 char* ap = aptr + indexA;
755 char* end = ap + Math.Min (lengthA, lengthB);
756 char* bp = bptr + indexB;
759 char c1 = Char.ToUpperInvariant (*ap);
760 char c2 = Char.ToUpperInvariant (*bp);
767 return lengthA - lengthB;
771 public bool EndsWith (String value)
774 throw new ArgumentNullException ("value");
776 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
784 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
787 throw new ArgumentNullException ("value");
789 culture = CultureInfo.CurrentCulture;
791 return culture.CompareInfo.IsSuffix (this, value,
792 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
795 // Following methods are culture-insensitive
796 public int IndexOfAny (char [] anyOf)
799 throw new ArgumentNullException ();
800 if (this.length == 0)
803 return IndexOfAnyUnchecked (anyOf, 0, this.length);
806 public int IndexOfAny (char [] anyOf, int startIndex)
809 throw new ArgumentNullException ();
810 if (startIndex < 0 || startIndex > this.length)
811 throw new ArgumentOutOfRangeException ();
813 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
816 public int IndexOfAny (char [] anyOf, int startIndex, int count)
819 throw new ArgumentNullException ();
820 if (startIndex < 0 || startIndex > this.length)
821 throw new ArgumentOutOfRangeException ();
822 if (count < 0 || startIndex > this.length - count)
823 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
825 return IndexOfAnyUnchecked (anyOf, startIndex, count);
828 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
830 if (anyOf.Length == 0)
833 if (anyOf.Length == 1)
834 return IndexOfUnchecked (anyOf[0], startIndex, count);
836 fixed (char* any = anyOf) {
840 char* end_any_ptr = any + anyOf.Length;
842 while (++any_ptr != end_any_ptr) {
843 if (*any_ptr > highest) {
848 if (*any_ptr < lowest)
852 fixed (char* start = &start_char) {
853 char* ptr = start + startIndex;
854 char* end_ptr = ptr + count;
856 while (ptr != end_ptr) {
857 if (*ptr > highest || *ptr < lowest) {
863 return (int)(ptr - start);
866 while (++any_ptr != end_any_ptr) {
867 if (*ptr == *any_ptr)
868 return (int)(ptr - start);
880 public int IndexOf (string value, StringComparison comparisonType)
882 return IndexOf (value, 0, this.Length, comparisonType);
885 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
887 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
890 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
892 switch (comparisonType) {
893 case StringComparison.CurrentCulture:
894 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
895 case StringComparison.CurrentCultureIgnoreCase:
896 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
897 case StringComparison.InvariantCulture:
898 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
899 case StringComparison.InvariantCultureIgnoreCase:
900 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
901 case StringComparison.Ordinal:
902 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
903 case StringComparison.OrdinalIgnoreCase:
904 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
906 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
907 throw new ArgumentException (msg, "comparisonType");
912 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
915 throw new ArgumentNullException ("value");
917 throw new ArgumentOutOfRangeException ("startIndex");
918 if (count < 0 || (this.length - startIndex) < count)
919 throw new ArgumentOutOfRangeException ("count");
921 if (options == CompareOptions.Ordinal)
922 return IndexOfOrdinalUnchecked (value, startIndex, count);
923 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
926 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
928 int valueLen = value.Length;
929 if (count < valueLen)
934 return IndexOfUnchecked (value[0], startIndex, count);
938 fixed (char* thisptr = this, valueptr = value) {
939 char* ap = thisptr + startIndex;
940 char* thisEnd = ap + count - valueLen + 1;
941 while (ap != thisEnd) {
942 if (*ap == *valueptr) {
943 for (int i = 1; i < valueLen; i++) {
944 if (ap[i] != valueptr[i])
947 return (int)(ap - thisptr);
956 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
958 int valueLen = value.Length;
959 if (count < valueLen)
965 fixed (char* thisptr = this, valueptr = value) {
966 char* ap = thisptr + startIndex;
967 char* thisEnd = ap + count - valueLen + 1;
968 while (ap != thisEnd) {
969 for (int i = 0; i < valueLen; i++) {
970 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
973 return (int)(ap - thisptr);
983 public int LastIndexOf (string value, StringComparison comparisonType)
985 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
988 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
990 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
993 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
995 switch (comparisonType) {
996 case StringComparison.CurrentCulture:
997 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
998 case StringComparison.CurrentCultureIgnoreCase:
999 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1000 case StringComparison.InvariantCulture:
1001 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1002 case StringComparison.InvariantCultureIgnoreCase:
1003 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1004 case StringComparison.Ordinal:
1005 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1006 case StringComparison.OrdinalIgnoreCase:
1007 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1009 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1010 throw new ArgumentException (msg, "comparisonType");
1015 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1018 throw new ArgumentNullException ("value");
1019 if (startIndex < 0 || startIndex > length)
1020 throw new ArgumentOutOfRangeException ("startIndex");
1021 if (count < 0 || (startIndex < count - 1))
1022 throw new ArgumentOutOfRangeException ("count");
1024 if (options == CompareOptions.Ordinal)
1025 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1026 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1029 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1031 int valueLen = value.Length;
1032 if (count < valueLen)
1035 if (valueLen <= 1) {
1037 return LastIndexOfUnchecked (value[0], startIndex, count);
1041 fixed (char* thisptr = this, valueptr = value) {
1042 char* ap = thisptr + startIndex - valueLen + 1;
1043 char* thisEnd = ap - count + valueLen - 1;
1044 while (ap != thisEnd) {
1045 if (*ap == *valueptr) {
1046 for (int i = 1; i < valueLen; i++) {
1047 if (ap[i] != valueptr[i])
1050 return (int)(ap - thisptr);
1059 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1061 int valueLen = value.Length;
1062 if (count < valueLen)
1068 fixed (char* thisptr = this, valueptr = value) {
1069 char* ap = thisptr + startIndex - valueLen + 1;
1070 char* thisEnd = ap - count + valueLen - 1;
1071 while (ap != thisEnd) {
1072 for (int i = 0; i < valueLen; i++) {
1073 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1076 return (int)(ap - thisptr);
1084 // Following methods are culture-insensitive
1085 public int IndexOf (char value)
1087 if (this.length == 0)
1090 return IndexOfUnchecked (value, 0, this.length);
1093 public int IndexOf (char value, int startIndex)
1096 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1097 if (startIndex > this.length)
1098 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1100 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1103 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1106 public int IndexOf (char value, int startIndex, int count)
1108 if (startIndex < 0 || startIndex > this.length)
1109 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1111 throw new ArgumentOutOfRangeException ("count", "< 0");
1112 if (startIndex > this.length - count)
1113 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1115 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1118 return IndexOfUnchecked (value, startIndex, count);
1121 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1123 // It helps JIT compiler to optimize comparison
1124 int value_32 = (int)value;
1126 fixed (char* start = &start_char) {
1127 char* ptr = start + startIndex;
1128 char* end_ptr = ptr + (count >> 3 << 3);
1130 while (ptr != end_ptr) {
1131 if (*ptr == value_32)
1132 return (int)(ptr - start);
1133 if (ptr[1] == value_32)
1134 return (int)(ptr - start + 1);
1135 if (ptr[2] == value_32)
1136 return (int)(ptr - start + 2);
1137 if (ptr[3] == value_32)
1138 return (int)(ptr - start + 3);
1139 if (ptr[4] == value_32)
1140 return (int)(ptr - start + 4);
1141 if (ptr[5] == value_32)
1142 return (int)(ptr - start + 5);
1143 if (ptr[6] == value_32)
1144 return (int)(ptr - start + 6);
1145 if (ptr[7] == value_32)
1146 return (int)(ptr - start + 7);
1151 end_ptr += count & 0x07;
1152 while (ptr != end_ptr) {
1153 if (*ptr == value_32)
1154 return (int)(ptr - start);
1162 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1166 int end = startIndex + count;
1167 char c = Char.ToUpperInvariant (value);
1168 fixed (char* s = &start_char) {
1169 for (int i = startIndex; i < end; i++)
1170 if (Char.ToUpperInvariant (s [i]) == c)
1176 // Following methods are culture-sensitive
1177 public int IndexOf (String value)
1180 throw new ArgumentNullException ("value");
1181 if (value.length == 0)
1183 if (this.length == 0)
1185 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1188 public int IndexOf (String value, int startIndex)
1190 return IndexOf (value, startIndex, this.length - startIndex);
1193 public int IndexOf (String value, int startIndex, int count)
1197 throw new ArgumentNullException ("value");
1199 throw new ArgumentNullException ("string2");
1201 if (startIndex < 0 || startIndex > this.length)
1202 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1203 if (count < 0 || startIndex > this.length - count)
1204 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1206 if (value.length == 0)
1209 if (startIndex == 0 && this.length == 0)
1215 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1218 // Following methods are culture-insensitive
1219 public int LastIndexOfAny (char [] anyOf)
1222 throw new ArgumentNullException ();
1224 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1227 public int LastIndexOfAny (char [] anyOf, int startIndex)
1230 throw new ArgumentNullException ();
1232 if (startIndex < 0 || startIndex >= this.length)
1233 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1235 if (this.length == 0)
1238 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1241 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1244 throw new ArgumentNullException ();
1246 if ((startIndex < 0) || (startIndex >= this.Length))
1247 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1248 if ((count < 0) || (count > this.Length))
1249 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1250 if (startIndex - count + 1 < 0)
1251 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1253 if (this.length == 0)
1256 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1259 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1261 if (anyOf.Length == 1)
1262 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1264 fixed (char* start = this, testStart = anyOf) {
1265 char* ptr = start + startIndex;
1266 char* ptrEnd = ptr - count;
1268 char* testEnd = testStart + anyOf.Length;
1270 while (ptr != ptrEnd) {
1272 while (test != testEnd) {
1274 return (int)(ptr - start);
1283 // Following methods are culture-insensitive
1284 public int LastIndexOf (char value)
1286 if (this.length == 0)
1289 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1292 public int LastIndexOf (char value, int startIndex)
1294 return LastIndexOf (value, startIndex, startIndex + 1);
1297 public int LastIndexOf (char value, int startIndex, int count)
1299 if (startIndex == 0 && this.length == 0)
1302 // >= for char (> for string)
1303 if ((startIndex < 0) || (startIndex >= this.Length))
1304 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1305 if ((count < 0) || (count > this.Length))
1306 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1307 if (startIndex - count + 1 < 0)
1308 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1310 return LastIndexOfUnchecked (value, startIndex, count);
1313 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1315 // It helps JIT compiler to optimize comparison
1316 int value_32 = (int)value;
1318 fixed (char* start = &start_char) {
1319 char* ptr = start + startIndex;
1320 char* end_ptr = ptr - (count >> 3 << 3);
1322 while (ptr != end_ptr) {
1323 if (*ptr == value_32)
1324 return (int)(ptr - start);
1325 if (ptr[-1] == value_32)
1326 return (int)(ptr - start) - 1;
1327 if (ptr[-2] == value_32)
1328 return (int)(ptr - start) - 2;
1329 if (ptr[-3] == value_32)
1330 return (int)(ptr - start) - 3;
1331 if (ptr[-4] == value_32)
1332 return (int)(ptr - start) - 4;
1333 if (ptr[-5] == value_32)
1334 return (int)(ptr - start) - 5;
1335 if (ptr[-6] == value_32)
1336 return (int)(ptr - start) - 6;
1337 if (ptr[-7] == value_32)
1338 return (int)(ptr - start) - 7;
1343 end_ptr -= count & 0x07;
1344 while (ptr != end_ptr) {
1345 if (*ptr == value_32)
1346 return (int)(ptr - start);
1354 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1358 int end = startIndex - count;
1359 char c = Char.ToUpperInvariant (value);
1360 fixed (char* s = &start_char) {
1361 for (int i = startIndex; i > end; i--)
1362 if (Char.ToUpperInvariant (s [i]) == c)
1368 // Following methods are culture-sensitive
1369 public int LastIndexOf (String value)
1371 if (this.length == 0)
1372 // This overload does additional checking
1373 return LastIndexOf (value, 0, 0);
1375 return LastIndexOf (value, this.length - 1, this.length);
1378 public int LastIndexOf (String value, int startIndex)
1380 int max = startIndex;
1381 if (max < this.Length)
1383 return LastIndexOf (value, startIndex, max);
1386 public int LastIndexOf (String value, int startIndex, int count)
1390 throw new ArgumentNullException ("value");
1392 throw new ArgumentNullException ("string2");
1395 // -1 > startIndex > for string (0 > startIndex >= for char)
1396 if ((startIndex < -1) || (startIndex > this.Length))
1397 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1398 if ((count < 0) || (count > this.Length))
1399 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1400 if (startIndex - count + 1 < 0)
1401 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1403 if (value.Length == 0)
1406 if (startIndex == 0 && this.length == 0)
1409 // This check is needed to match undocumented MS behaviour
1410 if (this.length == 0 && value.length > 0)
1416 if (startIndex == this.Length)
1418 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1422 public bool Contains (String value)
1424 return IndexOf (value) != -1;
1427 public static bool IsNullOrEmpty (String value)
1429 return (value == null) || (value.Length == 0);
1432 public string Normalize ()
1434 return Normalization.Normalize (this, 0);
1437 public string Normalize (NormalizationForm form)
1441 return Normalization.Normalize (this, 0);
1442 case NormalizationForm.FormD:
1443 return Normalization.Normalize (this, 1);
1444 case NormalizationForm.FormKC:
1445 return Normalization.Normalize (this, 2);
1446 case NormalizationForm.FormKD:
1447 return Normalization.Normalize (this, 3);
1451 public bool IsNormalized ()
1453 return Normalization.IsNormalized (this, 0);
1456 public bool IsNormalized (NormalizationForm form)
1460 return Normalization.IsNormalized (this, 0);
1461 case NormalizationForm.FormD:
1462 return Normalization.IsNormalized (this, 1);
1463 case NormalizationForm.FormKC:
1464 return Normalization.IsNormalized (this, 2);
1465 case NormalizationForm.FormKD:
1466 return Normalization.IsNormalized (this, 3);
1470 public string Remove (int startIndex)
1473 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1474 if (startIndex >= this.length)
1475 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1477 return Remove (startIndex, this.length - startIndex);
1481 public String PadLeft (int totalWidth)
1483 return PadLeft (totalWidth, ' ');
1486 public unsafe String PadLeft (int totalWidth, char paddingChar)
1488 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1491 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1493 if (totalWidth < this.length)
1496 String tmp = InternalAllocateStr (totalWidth);
1498 fixed (char* dest = tmp, src = this) {
1499 char* padPos = dest;
1500 char* padTo = dest + (totalWidth - length);
1501 while (padPos != padTo)
1502 *padPos++ = paddingChar;
1504 CharCopy (padTo, src, length);
1509 public String PadRight (int totalWidth)
1511 return PadRight (totalWidth, ' ');
1514 public unsafe String PadRight (int totalWidth, char paddingChar)
1516 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1519 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1521 if (totalWidth < this.length)
1524 String tmp = InternalAllocateStr (totalWidth);
1526 fixed (char* dest = tmp, src = this) {
1527 CharCopy (dest, src, length);
1529 char* padPos = dest + length;
1530 char* padTo = dest + totalWidth;
1531 while (padPos != padTo)
1532 *padPos++ = paddingChar;
1537 public bool StartsWith (String value)
1540 throw new ArgumentNullException ("value");
1542 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1546 [ComVisible (false)]
1547 public bool StartsWith (string value, StringComparison comparisonType)
1550 throw new ArgumentNullException ("value");
1552 switch (comparisonType) {
1553 case StringComparison.CurrentCulture:
1554 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1555 case StringComparison.CurrentCultureIgnoreCase:
1556 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1557 case StringComparison.InvariantCulture:
1558 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1559 case StringComparison.InvariantCultureIgnoreCase:
1560 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1561 case StringComparison.Ordinal:
1562 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1563 case StringComparison.OrdinalIgnoreCase:
1564 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1566 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1567 throw new ArgumentException (msg, "comparisonType");
1571 [ComVisible (false)]
1572 public bool EndsWith (string value, StringComparison comparisonType)
1575 throw new ArgumentNullException ("value");
1577 switch (comparisonType) {
1578 case StringComparison.CurrentCulture:
1579 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1580 case StringComparison.CurrentCultureIgnoreCase:
1581 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1582 case StringComparison.InvariantCulture:
1583 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1584 case StringComparison.InvariantCultureIgnoreCase:
1585 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1586 case StringComparison.Ordinal:
1587 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1588 case StringComparison.OrdinalIgnoreCase:
1589 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1591 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1592 throw new ArgumentException (msg, "comparisonType");
1602 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1604 if (culture == null)
1605 culture = CultureInfo.CurrentCulture;
1607 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1610 // Following method is culture-insensitive
1611 public unsafe String Replace (char oldChar, char newChar)
1613 if (this.length == 0 || oldChar == newChar)
1616 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1617 if (start_pos == -1)
1623 string tmp = InternalAllocateStr (length);
1624 fixed (char* dest = tmp, src = &start_char) {
1626 CharCopy (dest, src, start_pos);
1628 char* end_ptr = dest + length;
1629 char* dest_ptr = dest + start_pos;
1630 char* src_ptr = src + start_pos;
1632 while (dest_ptr != end_ptr) {
1633 if (*src_ptr == oldChar)
1634 *dest_ptr = newChar;
1636 *dest_ptr = *src_ptr;
1645 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1646 public String Replace (String oldValue, String newValue)
1648 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1649 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1651 if (oldValue == null)
1652 throw new ArgumentNullException ("oldValue");
1654 if (oldValue.Length == 0)
1655 throw new ArgumentException ("oldValue is the empty string.");
1657 if (this.Length == 0)
1660 if (newValue == null)
1661 newValue = String.Empty;
1663 return ReplaceUnchecked (oldValue, newValue);
1666 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1668 if (oldValue.length > length)
1670 if (oldValue.length == 1 && newValue.length == 1) {
1671 return Replace (oldValue[0], newValue[0]);
1672 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1673 // because the length of the result would be this.length and length calculation unneccesary
1676 const int maxValue = 200; // Allocate 800 byte maximum
1677 int* dat = stackalloc int[maxValue];
1678 fixed (char* source = this, replace = newValue) {
1679 int i = 0, count = 0;
1680 while (i < length) {
1681 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1685 if (count < maxValue)
1686 dat[count++] = found;
1688 return ReplaceFallback (oldValue, newValue, maxValue);
1690 i = found + oldValue.length;
1692 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1693 String tmp = InternalAllocateStr (nlen);
1695 int curPos = 0, lastReadPos = 0;
1696 fixed (char* dest = tmp) {
1697 for (int j = 0; j < count; j++) {
1698 int precopy = dat[j] - lastReadPos;
1699 CharCopy (dest + curPos, source + lastReadPos, precopy);
1701 lastReadPos = dat[j] + oldValue.length;
1702 CharCopy (dest + curPos, replace, newValue.length);
1703 curPos += newValue.length;
1705 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1711 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1713 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1714 StringBuilder sb = new StringBuilder (lengthEstimate);
1715 for (int i = 0; i < length;) {
1716 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1718 sb.Append (SubstringUnchecked (i, length - i));
1721 sb.Append (SubstringUnchecked (i, found - i));
1722 sb.Append (newValue);
1723 i = found + oldValue.Length;
1725 return sb.ToString ();
1729 public unsafe String Remove (int startIndex, int count)
1732 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1734 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1735 if (startIndex > this.length - count)
1736 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1738 String tmp = InternalAllocateStr (this.length - count);
1740 fixed (char *dest = tmp, src = this) {
1742 CharCopy (dst, src, startIndex);
1743 int skip = startIndex + count;
1745 CharCopy (dst, src + skip, length - skip);
1750 public String ToLower ()
1752 return ToLower (CultureInfo.CurrentCulture);
1755 public String ToLower (CultureInfo culture)
1757 if (culture == null)
1758 throw new ArgumentNullException ("culture");
1760 if (culture.LCID == 0x007F) // Invariant
1761 return ToLowerInvariant ();
1763 return culture.TextInfo.ToLower (this);
1767 public unsafe String ToLowerInvariant ()
1769 internal unsafe String ToLowerInvariant ()
1772 string tmp = InternalAllocateStr (length);
1773 fixed (char* source = &start_char, dest = tmp) {
1775 char* destPtr = (char*)dest;
1776 char* sourcePtr = (char*)source;
1778 for (int n = 0; n < length; n++) {
1779 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1787 public String ToUpper ()
1789 return ToUpper (CultureInfo.CurrentCulture);
1792 public String ToUpper (CultureInfo culture)
1794 if (culture == null)
1795 throw new ArgumentNullException ("culture");
1797 if (culture.LCID == 0x007F) // Invariant
1798 return ToUpperInvariant ();
1800 return culture.TextInfo.ToUpper (this);
1804 public unsafe String ToUpperInvariant ()
1806 internal unsafe String ToUpperInvariant ()
1809 string tmp = InternalAllocateStr (length);
1810 fixed (char* source = &start_char, dest = tmp) {
1812 char* destPtr = (char*)dest;
1813 char* sourcePtr = (char*)source;
1815 for (int n = 0; n < length; n++) {
1816 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1824 public override String ToString ()
1829 public String ToString (IFormatProvider provider)
1834 public static String Format (String format, Object arg0)
1836 return Format (null, format, new Object[] {arg0});
1839 public static String Format (String format, Object arg0, Object arg1)
1841 return Format (null, format, new Object[] {arg0, arg1});
1844 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1846 return Format (null, format, new Object[] {arg0, arg1, arg2});
1849 public static string Format (string format, params object[] args)
1851 return Format (null, format, args);
1854 public static string Format (IFormatProvider provider, string format, params object[] args)
1856 StringBuilder b = new StringBuilder ();
1857 FormatHelper (b, provider, format, args);
1858 return b.ToString ();
1861 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1864 throw new ArgumentNullException ("format");
1866 throw new ArgumentNullException ("args");
1870 while (ptr < format.length) {
1871 char c = format[ptr ++];
1874 result.Append (format, start, ptr - start - 1);
1876 // check for escaped open bracket
1878 if (format[ptr] == '{') {
1889 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1890 if (n >= args.Length)
1891 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1895 object arg = args[n];
1898 ICustomFormatter formatter = null;
1899 if (provider != null)
1900 formatter = provider.GetFormat (typeof (ICustomFormatter))
1901 as ICustomFormatter;
1904 else if (formatter != null)
1905 str = formatter.Format (arg_format, arg, provider);
1906 else if (arg is IFormattable)
1907 str = ((IFormattable)arg).ToString (arg_format, provider);
1909 str = arg.ToString ();
1911 // pad formatted string and append to result
1913 if (width > str.length) {
1914 const char padchar = ' ';
1915 int padlen = width - str.length;
1918 result.Append (str);
1919 result.Append (padchar, padlen);
1922 result.Append (padchar, padlen);
1923 result.Append (str);
1927 result.Append (str);
1931 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1932 result.Append (format, start, ptr - start - 1);
1935 else if (c == '}') {
1936 throw new FormatException ("Input string was not in a correct format.");
1940 if (start < format.length)
1941 result.Append (format, start, format.Length - start);
1944 public unsafe static String Copy (String str)
1947 throw new ArgumentNullException ("str");
1949 int length = str.length;
1951 String tmp = InternalAllocateStr (length);
1953 fixed (char *dest = tmp, src = str) {
1954 CharCopy (dest, src, length);
1960 public static String Concat (Object obj)
1963 return String.Empty;
1965 return obj.ToString ();
1968 public unsafe static String Concat (Object obj1, Object obj2)
1972 s1 = (obj1 != null) ? obj1.ToString () : null;
1973 s2 = (obj2 != null) ? obj2.ToString () : null;
1977 return String.Empty;
1980 } else if (s2 == null)
1983 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1984 if (s1.Length != 0) {
1985 fixed (char *dest = tmp, src = s1) {
1986 CharCopy (dest, src, s1.length);
1989 if (s2.Length != 0) {
1990 fixed (char *dest = tmp, src = s2) {
1991 CharCopy (dest + s1.Length, src, s2.length);
1998 public static String Concat (Object obj1, Object obj2, Object obj3)
2004 s1 = obj1.ToString ();
2009 s2 = obj2.ToString ();
2014 s3 = obj3.ToString ();
2016 return Concat (s1, s2, s3);
2019 #if ! BOOTSTRAP_WITH_OLDLIB
2020 [CLSCompliant(false)]
2021 public static String Concat (Object obj1, Object obj2, Object obj3,
2022 Object obj4, __arglist)
2024 string s1, s2, s3, s4;
2029 s1 = obj1.ToString ();
2034 s2 = obj2.ToString ();
2039 s3 = obj3.ToString ();
2041 ArgIterator iter = new ArgIterator (__arglist);
2042 int argCount = iter.GetRemainingCount();
2044 StringBuilder sb = new StringBuilder ();
2046 sb.Append (obj4.ToString ());
2048 for (int i = 0; i < argCount; i++) {
2049 TypedReference typedRef = iter.GetNextArg ();
2050 sb.Append (TypedReference.ToObject (typedRef));
2053 s4 = sb.ToString ();
2055 return Concat (s1, s2, s3, s4);
2059 public unsafe static String Concat (String s1, String s2)
2061 if (s1 == null || s1.Length == 0) {
2062 if (s2 == null || s2.Length == 0)
2063 return String.Empty;
2067 if (s2 == null || s2.Length == 0)
2070 String tmp = InternalAllocateStr (s1.length + s2.length);
2072 fixed (char *dest = tmp, src = s1)
2073 CharCopy (dest, src, s1.length);
2074 fixed (char *dest = tmp, src = s2)
2075 CharCopy (dest + s1.Length, src, s2.length);
2080 public unsafe static String Concat (String s1, String s2, String s3)
2082 if (s1 == null || s1.Length == 0){
2083 if (s2 == null || s2.Length == 0){
2084 if (s3 == null || s3.Length == 0)
2085 return String.Empty;
2088 if (s3 == null || s3.Length == 0)
2093 if (s2 == null || s2.Length == 0){
2094 if (s3 == null || s3.Length == 0)
2099 if (s3 == null || s3.Length == 0)
2104 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
2106 if (s1.Length != 0) {
2107 fixed (char *dest = tmp, src = s1) {
2108 CharCopy (dest, src, s1.length);
2111 if (s2.Length != 0) {
2112 fixed (char *dest = tmp, src = s2) {
2113 CharCopy (dest + s1.Length, src, s2.length);
2116 if (s3.Length != 0) {
2117 fixed (char *dest = tmp, src = s3) {
2118 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2125 public unsafe static String Concat (String s1, String s2, String s3, String s4)
2127 if (s1 == null && s2 == null && s3 == null && s4 == null)
2128 return String.Empty;
2139 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
2141 if (s1.Length != 0) {
2142 fixed (char *dest = tmp, src = s1) {
2143 CharCopy (dest, src, s1.length);
2146 if (s2.Length != 0) {
2147 fixed (char *dest = tmp, src = s2) {
2148 CharCopy (dest + s1.Length, src, s2.length);
2151 if (s3.Length != 0) {
2152 fixed (char *dest = tmp, src = s3) {
2153 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2156 if (s4.Length != 0) {
2157 fixed (char *dest = tmp, src = s4) {
2158 CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
2165 public static String Concat (params Object[] args)
2168 throw new ArgumentNullException ("args");
2170 int argLen = args.Length;
2172 return String.Empty;
2174 string [] strings = new string [argLen];
2176 for (int i = 0; i < argLen; i++) {
2177 if (args[i] != null) {
2178 strings[i] = args[i].ToString ();
2179 len += strings[i].length;
2183 return String.Empty;
2185 return ConcatInternal (strings, len);
2188 public static String Concat (params String[] values)
2191 throw new ArgumentNullException ("values");
2194 for (int i = 0; i < values.Length; i++) {
2195 String s = values[i];
2200 return String.Empty;
2202 return ConcatInternal (values, len);
2205 private static unsafe String ConcatInternal (String[] values, int length)
2207 String tmp = InternalAllocateStr (length);
2209 fixed (char* dest = tmp) {
2211 for (int i = 0; i < values.Length; i++) {
2212 String source = values[i];
2213 if (source != null) {
2214 fixed (char* src = source) {
2215 CharCopy (dest + pos, src, source.length);
2217 pos += source.Length;
2224 public unsafe String Insert (int startIndex, String value)
2227 throw new ArgumentNullException ("value");
2229 if (startIndex < 0 || startIndex > this.length)
2230 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2232 if (value.Length == 0)
2234 if (this.Length == 0)
2236 String tmp = InternalAllocateStr (this.length + value.length);
2238 fixed (char *dest = tmp, src = this, val = value) {
2240 CharCopy (dst, src, startIndex);
2242 CharCopy (dst, val, value.length);
2243 dst += value.length;
2244 CharCopy (dst, src + startIndex, length - startIndex);
2249 public static string Intern (string str)
2252 throw new ArgumentNullException ("str");
2254 return InternalIntern (str);
2257 public static string IsInterned (string str)
2260 throw new ArgumentNullException ("str");
2262 return InternalIsInterned (str);
2265 public static string Join (string separator, string [] value)
2268 throw new ArgumentNullException ("value");
2269 if (separator == null)
2270 separator = String.Empty;
2272 return JoinUnchecked (separator, value, 0, value.Length);
2275 public static string Join (string separator, string[] value, int startIndex, int count)
2278 throw new ArgumentNullException ("value");
2280 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2282 throw new ArgumentOutOfRangeException ("count", "< 0");
2283 if (startIndex > value.Length - count)
2284 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2286 if (startIndex == value.Length)
2287 return String.Empty;
2288 if (separator == null)
2289 separator = String.Empty;
2291 return JoinUnchecked (separator, value, startIndex, count);
2294 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2296 // Unchecked parameters
2297 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2298 // separator and value must not be null
2301 int maxIndex = startIndex + count;
2302 // Precount the number of characters that the resulting string will have
2303 for (int i = startIndex; i < maxIndex; i++) {
2304 String s = value[i];
2308 length += separator.length * (count - 1);
2310 return String.Empty;
2312 String tmp = InternalAllocateStr (length);
2315 fixed (char* dest = tmp, sepsrc = separator) {
2316 // Copy each string from value except the last one and add a separator for each
2318 for (int i = startIndex; i < maxIndex; i++) {
2319 String source = value[i];
2320 if (source != null) {
2321 if (source.Length > 0) {
2322 fixed (char* src = source)
2323 CharCopy (dest + pos, src, source.Length);
2324 pos += source.Length;
2327 if (separator.Length > 0) {
2328 CharCopy (dest + pos, sepsrc, separator.Length);
2329 pos += separator.Length;
2332 // Append last string that does not get an additional separator
2333 String sourceLast = value[maxIndex];
2334 if (sourceLast != null) {
2335 if (sourceLast.Length > 0) {
2336 fixed (char* src = sourceLast)
2337 CharCopy (dest + pos, src, sourceLast.Length);
2344 bool IConvertible.ToBoolean (IFormatProvider provider)
2346 return Convert.ToBoolean (this, provider);
2349 byte IConvertible.ToByte (IFormatProvider provider)
2351 return Convert.ToByte (this, provider);
2354 char IConvertible.ToChar (IFormatProvider provider)
2356 return Convert.ToChar (this, provider);
2359 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2361 return Convert.ToDateTime (this, provider);
2364 decimal IConvertible.ToDecimal (IFormatProvider provider)
2366 return Convert.ToDecimal (this, provider);
2369 double IConvertible.ToDouble (IFormatProvider provider)
2371 return Convert.ToDouble (this, provider);
2374 short IConvertible.ToInt16 (IFormatProvider provider)
2376 return Convert.ToInt16 (this, provider);
2379 int IConvertible.ToInt32 (IFormatProvider provider)
2381 return Convert.ToInt32 (this, provider);
2384 long IConvertible.ToInt64 (IFormatProvider provider)
2386 return Convert.ToInt64 (this, provider);
2389 sbyte IConvertible.ToSByte (IFormatProvider provider)
2391 return Convert.ToSByte (this, provider);
2394 float IConvertible.ToSingle (IFormatProvider provider)
2396 return Convert.ToSingle (this, provider);
2399 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2401 return Convert.ToType (this, conversionType, provider);
2404 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2406 return Convert.ToUInt16 (this, provider);
2409 uint IConvertible.ToUInt32 (IFormatProvider provider)
2411 return Convert.ToUInt32 (this, provider);
2414 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2416 return Convert.ToUInt64 (this, provider);
2425 public CharEnumerator GetEnumerator ()
2427 return new CharEnumerator (this);
2431 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2433 return new CharEnumerator (this);
2437 IEnumerator IEnumerable.GetEnumerator ()
2439 return new CharEnumerator (this);
2442 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2443 out bool left_align, out string format)
2445 // parses format specifier of form:
2451 // N = argument number (non-negative integer)
2453 n = ParseDecimal (str, ref ptr);
2455 throw new FormatException ("Input string was not in a correct format.");
2457 // M = width (non-negative integer)
2459 if (str[ptr] == ',') {
2460 // White space between ',' and number or sign.
2462 while (Char.IsWhiteSpace (str [ptr]))
2466 format = str.Substring (start, ptr - start);
2468 left_align = (str [ptr] == '-');
2472 width = ParseDecimal (str, ref ptr);
2474 throw new FormatException ("Input string was not in a correct format.");
2479 format = String.Empty;
2482 // F = argument format (string)
2484 if (str[ptr] == ':') {
2486 while (str[ptr] != '}')
2489 format += str.Substring (start, ptr - start);
2494 if (str[ptr ++] != '}')
2495 throw new FormatException ("Input string was not in a correct format.");
2497 catch (IndexOutOfRangeException) {
2498 throw new FormatException ("Input string was not in a correct format.");
2502 private static int ParseDecimal (string str, ref int ptr)
2508 if (c < '0' || '9' < c)
2511 n = n * 10 + c - '0';
2522 internal unsafe void InternalSetChar (int idx, char val)
2524 if ((uint) idx >= (uint) Length)
2525 throw new ArgumentOutOfRangeException ("idx");
2527 fixed (char * pStr = &start_char)
2533 internal unsafe void InternalSetLength (int newLength)
2535 if (newLength > length)
2536 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2538 // zero terminate, we can pass string objects directly via pinvoke
2539 // we also zero the rest of the string, since the new GC needs to be
2540 // able to handle the changing size (it will skip the 0 bytes).
2541 fixed (char * pStr = &start_char) {
2542 char *p = pStr + newLength;
2543 char *end = pStr + length;
2553 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2555 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2556 public unsafe override int GetHashCode ()
2558 fixed (char * c = this) {
2560 char * end = cc + length - 1;
2562 for (;cc < end; cc += 2) {
2563 h = (h << 5) - h + *cc;
2564 h = (h << 5) - h + cc [1];
2568 h = (h << 5) - h + *cc;
2573 internal unsafe int GetCaseInsensitiveHashCode ()
2575 fixed (char * c = this) {
2577 char * end = cc + length - 1;
2579 for (;cc < end; cc += 2) {
2580 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2581 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2585 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2590 // Certain constructors are redirected to CreateString methods with
2591 // matching argument list. The this pointer should not be used.
2593 private unsafe String CreateString (sbyte* value)
2596 return String.Empty;
2598 byte* bytes = (byte*) value;
2602 while (bytes++ [0] != 0)
2604 } catch (NullReferenceException) {
2605 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2607 } catch (AccessViolationException) {
2608 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2612 return CreateString (value, 0, length, null);
2615 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2617 return CreateString (value, startIndex, length, null);
2620 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2623 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2625 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2626 if (value + startIndex < value)
2627 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2629 bool isDefaultEncoding;
2631 if (isDefaultEncoding = (enc == null)) {
2634 throw new ArgumentNullException ("value");
2637 if (value == null || length == 0)
2639 return String.Empty;
2641 enc = Encoding.Default;
2644 byte [] bytes = new byte [length];
2647 fixed (byte* bytePtr = bytes)
2649 memcpy (bytePtr, (byte*) (value + startIndex), length);
2650 } catch (NullReferenceException) {
2652 if (!isDefaultEncoding)
2656 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2658 } catch (AccessViolationException) {
2659 if (!isDefaultEncoding)
2662 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2666 // GetString () is called even when length == 0
2667 return enc.GetString (bytes);
2670 unsafe string CreateString (char *value)
2673 return string.Empty;
2680 string result = InternalAllocateStr (i);
2683 fixed (char *dest = result) {
2684 CharCopy (dest, value, i);
2690 unsafe string CreateString (char *value, int startIndex, int length)
2693 return string.Empty;
2695 throw new ArgumentNullException ("value");
2697 throw new ArgumentOutOfRangeException ("startIndex");
2699 throw new ArgumentOutOfRangeException ("length");
2701 string result = InternalAllocateStr (length);
2703 fixed (char *dest = result) {
2704 CharCopy (dest, value + startIndex, length);
2709 unsafe string CreateString (char [] val, int startIndex, int length)
2712 throw new ArgumentNullException ("value");
2714 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2716 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2717 if (startIndex > val.Length - length)
2718 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2720 return string.Empty;
2722 string result = InternalAllocateStr (length);
2724 fixed (char *dest = result, src = val) {
2725 CharCopy (dest, src + startIndex, length);
2730 unsafe string CreateString (char [] val)
2733 return string.Empty;
2734 if (val.Length == 0)
2735 return string.Empty;
2736 string result = InternalAllocateStr (val.Length);
2738 fixed (char *dest = result, src = val) {
2739 CharCopy (dest, src, val.Length);
2744 unsafe string CreateString (char c, int count)
2747 throw new ArgumentOutOfRangeException ("count");
2749 return string.Empty;
2750 string result = InternalAllocateStr (count);
2751 fixed (char *dest = result) {
2753 char *end = p + count;
2762 /* helpers used by the runtime as well as above or eslewhere in corlib */
2763 internal static unsafe void memset (byte *dest, int val, int len)
2774 val = val | (val << 8);
2775 val = val | (val << 16);
2778 int rest = (int)dest & 3;
2786 } while (rest != 0);
2789 ((int*)dest) [0] = val;
2790 ((int*)dest) [1] = val;
2791 ((int*)dest) [2] = val;
2792 ((int*)dest) [3] = val;
2797 ((int*)dest) [0] = val;
2809 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2810 /*while (size >= 32) {
2811 // using long is better than int and slower than double
2812 // FIXME: enable this only on correct alignment or on platforms
2813 // that can tolerate unaligned reads/writes of doubles
2814 ((double*)dest) [0] = ((double*)src) [0];
2815 ((double*)dest) [1] = ((double*)src) [1];
2816 ((double*)dest) [2] = ((double*)src) [2];
2817 ((double*)dest) [3] = ((double*)src) [3];
2822 while (size >= 16) {
2823 ((int*)dest) [0] = ((int*)src) [0];
2824 ((int*)dest) [1] = ((int*)src) [1];
2825 ((int*)dest) [2] = ((int*)src) [2];
2826 ((int*)dest) [3] = ((int*)src) [3];
2832 ((int*)dest) [0] = ((int*)src) [0];
2838 ((byte*)dest) [0] = ((byte*)src) [0];
2844 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2846 ((short*)dest) [0] = ((short*)src) [0];
2847 ((short*)dest) [1] = ((short*)src) [1];
2848 ((short*)dest) [2] = ((short*)src) [2];
2849 ((short*)dest) [3] = ((short*)src) [3];
2855 ((short*)dest) [0] = ((short*)src) [0];
2861 ((byte*)dest) [0] = ((byte*)src) [0];
2863 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2865 ((byte*)dest) [0] = ((byte*)src) [0];
2866 ((byte*)dest) [1] = ((byte*)src) [1];
2867 ((byte*)dest) [2] = ((byte*)src) [2];
2868 ((byte*)dest) [3] = ((byte*)src) [3];
2869 ((byte*)dest) [4] = ((byte*)src) [4];
2870 ((byte*)dest) [5] = ((byte*)src) [5];
2871 ((byte*)dest) [6] = ((byte*)src) [6];
2872 ((byte*)dest) [7] = ((byte*)src) [7];
2878 ((byte*)dest) [0] = ((byte*)src) [0];
2879 ((byte*)dest) [1] = ((byte*)src) [1];
2885 ((byte*)dest) [0] = ((byte*)src) [0];
2888 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2889 // FIXME: if pointers are not aligned, try to align them
2890 // so a faster routine can be used. Handle the case where
2891 // the pointers can't be reduced to have the same alignment
2892 // (just ignore the issue on x86?)
2893 if ((((int)dest | (int)src) & 3) != 0) {
2894 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2900 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2901 ((short*)dest) [0] = ((short*)src) [0];
2906 if ((((int)dest | (int)src) & 1) != 0) {
2907 memcpy1 (dest, src, size);
2910 if ((((int)dest | (int)src) & 2) != 0) {
2911 memcpy2 (dest, src, size);
2915 memcpy4 (dest, src, size);
2918 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2919 // Same rules as for memcpy, but with the premise that
2920 // chars can only be aligned to even addresses if their
2921 // enclosing types are correctly aligned
2922 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2923 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2924 ((short*)dest) [0] = ((short*)src) [0];
2929 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2930 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2934 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2937 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2941 for (int i = count; i > 0; i--) {
2948 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2950 fixed (char* dest = target, src = source)
2951 CharCopy (dest + targetIndex, src + sourceIndex, count);
2954 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2956 fixed (char* dest = target, src = source)
2957 CharCopy (dest + targetIndex, src + sourceIndex, count);
2960 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2961 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2963 fixed (char* dest = target, src = source)
2964 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2967 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2968 unsafe public extern String (char *value);
2970 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2971 unsafe public extern String (char *value, int startIndex, int length);
2973 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2974 unsafe public extern String (sbyte *value);
2976 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2977 unsafe public extern String (sbyte *value, int startIndex, int length);
2979 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2980 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2982 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2983 public extern String (char [] val, int startIndex, int length);
2985 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2986 public extern String (char [] val);
2988 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2989 public extern String (char c, int count);
2991 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2992 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2994 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2995 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2997 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2998 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3000 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3001 private extern String[] InternalSplit (char[] separator, int count, int options);
3003 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3004 private extern String InternalTrim (char[] chars, int typ);
3006 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3007 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3009 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3010 private extern String InternalPad (int width, char chr, bool right);
3012 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3013 internal extern static String InternalAllocateStr (int length);
3015 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3016 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3018 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3019 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3021 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3022 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3024 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3025 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3027 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3028 private extern static string InternalIntern (string str);
3030 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3031 private extern static string InternalIsInterned (string str);