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;
638 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
641 throw new ArgumentNullException ("culture");
643 return culture.CompareInfo.Compare (strA, strB, options);
646 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
649 throw new ArgumentNullException ("culture");
654 if (length > (strA.Length - indexA))
655 len1 = strA.Length - indexA;
657 if (length > (strB.Length - indexB))
658 len2 = strB.Length - indexB;
660 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
664 public int CompareTo (Object value)
669 if (!(value is String))
670 throw new ArgumentException ();
672 return String.Compare (this, (String) value);
675 public int CompareTo (String strB)
680 return Compare (this, strB);
683 public static int CompareOrdinal (String strA, String strB)
685 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
688 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
690 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
691 throw new ArgumentOutOfRangeException ();
693 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
696 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
698 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
699 throw new ArgumentOutOfRangeException ();
701 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
704 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
711 } else if (strB == null) {
714 int lengthA = Math.Min (lenA, strA.Length - indexA);
715 int lengthB = Math.Min (lenB, strB.Length - indexB);
717 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
720 fixed (char* aptr = strA, bptr = strB) {
721 char* ap = aptr + indexA;
722 char* end = ap + Math.Min (lengthA, lengthB);
723 char* bp = bptr + indexB;
730 return lengthA - lengthB;
734 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
736 // Same as above, but checks versus uppercase characters
742 } else if (strB == null) {
745 int lengthA = Math.Min (lenA, strA.Length - indexA);
746 int lengthB = Math.Min (lenB, strB.Length - indexB);
748 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
751 fixed (char* aptr = strA, bptr = strB) {
752 char* ap = aptr + indexA;
753 char* end = ap + Math.Min (lengthA, lengthB);
754 char* bp = bptr + indexB;
757 char c1 = Char.ToUpperInvariant (*ap);
758 char c2 = Char.ToUpperInvariant (*bp);
765 return lengthA - lengthB;
769 public bool EndsWith (String value)
772 throw new ArgumentNullException ("value");
774 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
782 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
785 throw new ArgumentNullException ("value");
787 culture = CultureInfo.CurrentCulture;
789 return culture.CompareInfo.IsSuffix (this, value,
790 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
793 // Following methods are culture-insensitive
794 public int IndexOfAny (char [] anyOf)
797 throw new ArgumentNullException ();
798 if (this.length == 0)
801 return IndexOfAnyUnchecked (anyOf, 0, this.length);
804 public int IndexOfAny (char [] anyOf, int startIndex)
807 throw new ArgumentNullException ();
808 if (startIndex < 0 || startIndex > this.length)
809 throw new ArgumentOutOfRangeException ();
811 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
814 public int IndexOfAny (char [] anyOf, int startIndex, int count)
817 throw new ArgumentNullException ();
818 if (startIndex < 0 || startIndex > this.length)
819 throw new ArgumentOutOfRangeException ();
820 if (count < 0 || startIndex > this.length - count)
821 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
823 return IndexOfAnyUnchecked (anyOf, startIndex, count);
826 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
828 if (anyOf.Length == 0)
831 if (anyOf.Length == 1)
832 return IndexOfUnchecked (anyOf[0], startIndex, count);
834 fixed (char* any = anyOf) {
838 char* end_any_ptr = any + anyOf.Length;
840 while (++any_ptr != end_any_ptr) {
841 if (*any_ptr > highest) {
846 if (*any_ptr < lowest)
850 fixed (char* start = &start_char) {
851 char* ptr = start + startIndex;
852 char* end_ptr = ptr + count;
854 while (ptr != end_ptr) {
855 if (*ptr > highest || *ptr < lowest) {
861 return (int)(ptr - start);
864 while (++any_ptr != end_any_ptr) {
865 if (*ptr == *any_ptr)
866 return (int)(ptr - start);
878 public int IndexOf (string value, StringComparison comparisonType)
880 return IndexOf (value, 0, this.Length, comparisonType);
883 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
885 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
888 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
890 switch (comparisonType) {
891 case StringComparison.CurrentCulture:
892 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
893 case StringComparison.CurrentCultureIgnoreCase:
894 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
895 case StringComparison.InvariantCulture:
896 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
897 case StringComparison.InvariantCultureIgnoreCase:
898 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
899 case StringComparison.Ordinal:
900 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
901 case StringComparison.OrdinalIgnoreCase:
902 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
904 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
905 throw new ArgumentException (msg, "comparisonType");
910 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
913 throw new ArgumentNullException ("value");
915 throw new ArgumentOutOfRangeException ("startIndex");
916 if (count < 0 || (this.length - startIndex) < count)
917 throw new ArgumentOutOfRangeException ("count");
919 if (options == CompareOptions.Ordinal)
920 return IndexOfOrdinalUnchecked (value, startIndex, count);
921 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
924 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
926 int valueLen = value.Length;
927 if (count < valueLen)
932 return IndexOfUnchecked (value[0], startIndex, count);
936 fixed (char* thisptr = this, valueptr = value) {
937 char* ap = thisptr + startIndex;
938 char* thisEnd = ap + count - valueLen + 1;
939 while (ap != thisEnd) {
940 if (*ap == *valueptr) {
941 for (int i = 1; i < valueLen; i++) {
942 if (ap[i] != valueptr[i])
945 return (int)(ap - thisptr);
954 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
956 int valueLen = value.Length;
957 if (count < valueLen)
963 fixed (char* thisptr = this, valueptr = value) {
964 char* ap = thisptr + startIndex;
965 char* thisEnd = ap + count - valueLen + 1;
966 while (ap != thisEnd) {
967 for (int i = 0; i < valueLen; i++) {
968 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
971 return (int)(ap - thisptr);
981 public int LastIndexOf (string value, StringComparison comparisonType)
983 if (this.Length == 0)
984 return value == String.Empty ? 0 : -1;
986 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
989 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
991 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
994 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
996 switch (comparisonType) {
997 case StringComparison.CurrentCulture:
998 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
999 case StringComparison.CurrentCultureIgnoreCase:
1000 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1001 case StringComparison.InvariantCulture:
1002 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1003 case StringComparison.InvariantCultureIgnoreCase:
1004 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1005 case StringComparison.Ordinal:
1006 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1007 case StringComparison.OrdinalIgnoreCase:
1008 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1010 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1011 throw new ArgumentException (msg, "comparisonType");
1016 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1019 throw new ArgumentNullException ("value");
1020 if (startIndex < 0 || startIndex > length)
1021 throw new ArgumentOutOfRangeException ("startIndex");
1022 if (count < 0 || (startIndex < count - 1))
1023 throw new ArgumentOutOfRangeException ("count");
1025 if (options == CompareOptions.Ordinal)
1026 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1027 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1030 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1032 int valueLen = value.Length;
1033 if (count < valueLen)
1036 if (valueLen <= 1) {
1038 return LastIndexOfUnchecked (value[0], startIndex, count);
1042 fixed (char* thisptr = this, valueptr = value) {
1043 char* ap = thisptr + startIndex - valueLen + 1;
1044 char* thisEnd = ap - count + valueLen - 1;
1045 while (ap != thisEnd) {
1046 if (*ap == *valueptr) {
1047 for (int i = 1; i < valueLen; i++) {
1048 if (ap[i] != valueptr[i])
1051 return (int)(ap - thisptr);
1060 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1062 int valueLen = value.Length;
1063 if (count < valueLen)
1069 fixed (char* thisptr = this, valueptr = value) {
1070 char* ap = thisptr + startIndex - valueLen + 1;
1071 char* thisEnd = ap - count + valueLen - 1;
1072 while (ap != thisEnd) {
1073 for (int i = 0; i < valueLen; i++) {
1074 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1077 return (int)(ap - thisptr);
1085 // Following methods are culture-insensitive
1086 public int IndexOf (char value)
1088 if (this.length == 0)
1091 return IndexOfUnchecked (value, 0, this.length);
1094 public int IndexOf (char value, int startIndex)
1097 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1098 if (startIndex > this.length)
1099 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1101 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1104 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1107 public int IndexOf (char value, int startIndex, int count)
1109 if (startIndex < 0 || startIndex > this.length)
1110 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1112 throw new ArgumentOutOfRangeException ("count", "< 0");
1113 if (startIndex > this.length - count)
1114 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1116 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1119 return IndexOfUnchecked (value, startIndex, count);
1122 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1124 // It helps JIT compiler to optimize comparison
1125 int value_32 = (int)value;
1127 fixed (char* start = &start_char) {
1128 char* ptr = start + startIndex;
1129 char* end_ptr = ptr + (count >> 3 << 3);
1131 while (ptr != end_ptr) {
1132 if (*ptr == value_32)
1133 return (int)(ptr - start);
1134 if (ptr[1] == value_32)
1135 return (int)(ptr - start + 1);
1136 if (ptr[2] == value_32)
1137 return (int)(ptr - start + 2);
1138 if (ptr[3] == value_32)
1139 return (int)(ptr - start + 3);
1140 if (ptr[4] == value_32)
1141 return (int)(ptr - start + 4);
1142 if (ptr[5] == value_32)
1143 return (int)(ptr - start + 5);
1144 if (ptr[6] == value_32)
1145 return (int)(ptr - start + 6);
1146 if (ptr[7] == value_32)
1147 return (int)(ptr - start + 7);
1152 end_ptr += count & 0x07;
1153 while (ptr != end_ptr) {
1154 if (*ptr == value_32)
1155 return (int)(ptr - start);
1163 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1167 int end = startIndex + count;
1168 char c = Char.ToUpperInvariant (value);
1169 fixed (char* s = &start_char) {
1170 for (int i = startIndex; i < end; i++)
1171 if (Char.ToUpperInvariant (s [i]) == c)
1177 // Following methods are culture-sensitive
1178 public int IndexOf (String value)
1181 throw new ArgumentNullException ("value");
1182 if (value.length == 0)
1184 if (this.length == 0)
1186 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1189 public int IndexOf (String value, int startIndex)
1191 return IndexOf (value, startIndex, this.length - startIndex);
1194 public int IndexOf (String value, int startIndex, int count)
1198 throw new ArgumentNullException ("value");
1200 throw new ArgumentNullException ("string2");
1202 if (startIndex < 0 || startIndex > this.length)
1203 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1204 if (count < 0 || startIndex > this.length - count)
1205 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1207 if (value.length == 0)
1210 if (startIndex == 0 && this.length == 0)
1216 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1219 // Following methods are culture-insensitive
1220 public int LastIndexOfAny (char [] anyOf)
1223 throw new ArgumentNullException ();
1225 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1228 public int LastIndexOfAny (char [] anyOf, int startIndex)
1231 throw new ArgumentNullException ();
1233 if (startIndex < 0 || startIndex >= this.length)
1234 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1236 if (this.length == 0)
1239 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1242 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1245 throw new ArgumentNullException ();
1247 if ((startIndex < 0) || (startIndex >= this.Length))
1248 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1249 if ((count < 0) || (count > this.Length))
1250 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1251 if (startIndex - count + 1 < 0)
1252 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1254 if (this.length == 0)
1257 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1260 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1262 if (anyOf.Length == 1)
1263 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1265 fixed (char* start = this, testStart = anyOf) {
1266 char* ptr = start + startIndex;
1267 char* ptrEnd = ptr - count;
1269 char* testEnd = testStart + anyOf.Length;
1271 while (ptr != ptrEnd) {
1273 while (test != testEnd) {
1275 return (int)(ptr - start);
1284 // Following methods are culture-insensitive
1285 public int LastIndexOf (char value)
1287 if (this.length == 0)
1290 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1293 public int LastIndexOf (char value, int startIndex)
1295 return LastIndexOf (value, startIndex, startIndex + 1);
1298 public int LastIndexOf (char value, int startIndex, int count)
1300 if (startIndex == 0 && this.length == 0)
1303 // >= for char (> for string)
1304 if ((startIndex < 0) || (startIndex >= this.Length))
1305 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1306 if ((count < 0) || (count > this.Length))
1307 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1308 if (startIndex - count + 1 < 0)
1309 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1311 return LastIndexOfUnchecked (value, startIndex, count);
1314 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1316 // It helps JIT compiler to optimize comparison
1317 int value_32 = (int)value;
1319 fixed (char* start = &start_char) {
1320 char* ptr = start + startIndex;
1321 char* end_ptr = ptr - (count >> 3 << 3);
1323 while (ptr != end_ptr) {
1324 if (*ptr == value_32)
1325 return (int)(ptr - start);
1326 if (ptr[-1] == value_32)
1327 return (int)(ptr - start) - 1;
1328 if (ptr[-2] == value_32)
1329 return (int)(ptr - start) - 2;
1330 if (ptr[-3] == value_32)
1331 return (int)(ptr - start) - 3;
1332 if (ptr[-4] == value_32)
1333 return (int)(ptr - start) - 4;
1334 if (ptr[-5] == value_32)
1335 return (int)(ptr - start) - 5;
1336 if (ptr[-6] == value_32)
1337 return (int)(ptr - start) - 6;
1338 if (ptr[-7] == value_32)
1339 return (int)(ptr - start) - 7;
1344 end_ptr -= count & 0x07;
1345 while (ptr != end_ptr) {
1346 if (*ptr == value_32)
1347 return (int)(ptr - start);
1355 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1359 int end = startIndex - count;
1360 char c = Char.ToUpperInvariant (value);
1361 fixed (char* s = &start_char) {
1362 for (int i = startIndex; i > end; i--)
1363 if (Char.ToUpperInvariant (s [i]) == c)
1369 // Following methods are culture-sensitive
1370 public int LastIndexOf (String value)
1372 if (this.length == 0)
1373 // This overload does additional checking
1374 return LastIndexOf (value, 0, 0);
1376 return LastIndexOf (value, this.length - 1, this.length);
1379 public int LastIndexOf (String value, int startIndex)
1381 int max = startIndex;
1382 if (max < this.Length)
1384 return LastIndexOf (value, startIndex, max);
1387 public int LastIndexOf (String value, int startIndex, int count)
1391 throw new ArgumentNullException ("value");
1393 throw new ArgumentNullException ("string2");
1396 // -1 > startIndex > for string (0 > startIndex >= for char)
1397 if ((startIndex < -1) || (startIndex > this.Length))
1398 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1399 if ((count < 0) || (count > this.Length))
1400 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1401 if (startIndex - count + 1 < 0)
1402 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1404 if (value.Length == 0)
1407 if (startIndex == 0 && this.length == 0)
1410 // This check is needed to match undocumented MS behaviour
1411 if (this.length == 0 && value.length > 0)
1417 if (startIndex == this.Length)
1419 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1423 public bool Contains (String value)
1425 return IndexOf (value) != -1;
1428 public static bool IsNullOrEmpty (String value)
1430 return (value == null) || (value.Length == 0);
1433 public string Normalize ()
1435 return Normalization.Normalize (this, 0);
1438 public string Normalize (NormalizationForm normalizationForm)
1440 switch (normalizationForm) {
1442 return Normalization.Normalize (this, 0);
1443 case NormalizationForm.FormD:
1444 return Normalization.Normalize (this, 1);
1445 case NormalizationForm.FormKC:
1446 return Normalization.Normalize (this, 2);
1447 case NormalizationForm.FormKD:
1448 return Normalization.Normalize (this, 3);
1452 public bool IsNormalized ()
1454 return Normalization.IsNormalized (this, 0);
1457 public bool IsNormalized (NormalizationForm normalizationForm)
1459 switch (normalizationForm) {
1461 return Normalization.IsNormalized (this, 0);
1462 case NormalizationForm.FormD:
1463 return Normalization.IsNormalized (this, 1);
1464 case NormalizationForm.FormKC:
1465 return Normalization.IsNormalized (this, 2);
1466 case NormalizationForm.FormKD:
1467 return Normalization.IsNormalized (this, 3);
1471 public string Remove (int startIndex)
1474 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1475 if (startIndex >= this.length)
1476 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1478 return Remove (startIndex, this.length - startIndex);
1482 public String PadLeft (int totalWidth)
1484 return PadLeft (totalWidth, ' ');
1487 public unsafe String PadLeft (int totalWidth, char paddingChar)
1489 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1492 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1494 if (totalWidth < this.length)
1497 String tmp = InternalAllocateStr (totalWidth);
1499 fixed (char* dest = tmp, src = this) {
1500 char* padPos = dest;
1501 char* padTo = dest + (totalWidth - length);
1502 while (padPos != padTo)
1503 *padPos++ = paddingChar;
1505 CharCopy (padTo, src, length);
1510 public String PadRight (int totalWidth)
1512 return PadRight (totalWidth, ' ');
1515 public unsafe String PadRight (int totalWidth, char paddingChar)
1517 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1520 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1522 if (totalWidth < this.length)
1525 String tmp = InternalAllocateStr (totalWidth);
1527 fixed (char* dest = tmp, src = this) {
1528 CharCopy (dest, src, length);
1530 char* padPos = dest + length;
1531 char* padTo = dest + totalWidth;
1532 while (padPos != padTo)
1533 *padPos++ = paddingChar;
1538 public bool StartsWith (String value)
1541 throw new ArgumentNullException ("value");
1543 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1547 [ComVisible (false)]
1548 public bool StartsWith (string value, StringComparison comparisonType)
1551 throw new ArgumentNullException ("value");
1553 switch (comparisonType) {
1554 case StringComparison.CurrentCulture:
1555 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1556 case StringComparison.CurrentCultureIgnoreCase:
1557 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1558 case StringComparison.InvariantCulture:
1559 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1560 case StringComparison.InvariantCultureIgnoreCase:
1561 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1562 case StringComparison.Ordinal:
1563 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1564 case StringComparison.OrdinalIgnoreCase:
1565 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1567 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1568 throw new ArgumentException (msg, "comparisonType");
1572 [ComVisible (false)]
1573 public bool EndsWith (string value, StringComparison comparisonType)
1576 throw new ArgumentNullException ("value");
1578 switch (comparisonType) {
1579 case StringComparison.CurrentCulture:
1580 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1581 case StringComparison.CurrentCultureIgnoreCase:
1582 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1583 case StringComparison.InvariantCulture:
1584 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1585 case StringComparison.InvariantCultureIgnoreCase:
1586 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1587 case StringComparison.Ordinal:
1588 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1589 case StringComparison.OrdinalIgnoreCase:
1590 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1592 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1593 throw new ArgumentException (msg, "comparisonType");
1603 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1605 if (culture == null)
1606 culture = CultureInfo.CurrentCulture;
1608 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1611 // Following method is culture-insensitive
1612 public unsafe String Replace (char oldChar, char newChar)
1614 if (this.length == 0 || oldChar == newChar)
1617 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1618 if (start_pos == -1)
1624 string tmp = InternalAllocateStr (length);
1625 fixed (char* dest = tmp, src = &start_char) {
1627 CharCopy (dest, src, start_pos);
1629 char* end_ptr = dest + length;
1630 char* dest_ptr = dest + start_pos;
1631 char* src_ptr = src + start_pos;
1633 while (dest_ptr != end_ptr) {
1634 if (*src_ptr == oldChar)
1635 *dest_ptr = newChar;
1637 *dest_ptr = *src_ptr;
1646 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1647 public String Replace (String oldValue, String newValue)
1649 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1650 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1652 if (oldValue == null)
1653 throw new ArgumentNullException ("oldValue");
1655 if (oldValue.Length == 0)
1656 throw new ArgumentException ("oldValue is the empty string.");
1658 if (this.Length == 0)
1661 if (newValue == null)
1662 newValue = String.Empty;
1664 return ReplaceUnchecked (oldValue, newValue);
1667 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1669 if (oldValue.length > length)
1671 if (oldValue.length == 1 && newValue.length == 1) {
1672 return Replace (oldValue[0], newValue[0]);
1673 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1674 // because the length of the result would be this.length and length calculation unneccesary
1677 const int maxValue = 200; // Allocate 800 byte maximum
1678 int* dat = stackalloc int[maxValue];
1679 fixed (char* source = this, replace = newValue) {
1680 int i = 0, count = 0;
1681 while (i < length) {
1682 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1686 if (count < maxValue)
1687 dat[count++] = found;
1689 return ReplaceFallback (oldValue, newValue, maxValue);
1691 i = found + oldValue.length;
1695 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1696 String tmp = InternalAllocateStr (nlen);
1698 int curPos = 0, lastReadPos = 0;
1699 fixed (char* dest = tmp) {
1700 for (int j = 0; j < count; j++) {
1701 int precopy = dat[j] - lastReadPos;
1702 CharCopy (dest + curPos, source + lastReadPos, precopy);
1704 lastReadPos = dat[j] + oldValue.length;
1705 CharCopy (dest + curPos, replace, newValue.length);
1706 curPos += newValue.length;
1708 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1714 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1716 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1717 StringBuilder sb = new StringBuilder (lengthEstimate);
1718 for (int i = 0; i < length;) {
1719 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1721 sb.Append (SubstringUnchecked (i, length - i));
1724 sb.Append (SubstringUnchecked (i, found - i));
1725 sb.Append (newValue);
1726 i = found + oldValue.Length;
1728 return sb.ToString ();
1732 public unsafe String Remove (int startIndex, int count)
1735 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1737 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1738 if (startIndex > this.length - count)
1739 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1741 String tmp = InternalAllocateStr (this.length - count);
1743 fixed (char *dest = tmp, src = this) {
1745 CharCopy (dst, src, startIndex);
1746 int skip = startIndex + count;
1748 CharCopy (dst, src + skip, length - skip);
1753 public String ToLower ()
1755 return ToLower (CultureInfo.CurrentCulture);
1758 public String ToLower (CultureInfo culture)
1760 if (culture == null)
1761 throw new ArgumentNullException ("culture");
1763 if (culture.LCID == 0x007F) // Invariant
1764 return ToLowerInvariant ();
1766 return culture.TextInfo.ToLower (this);
1770 public unsafe String ToLowerInvariant ()
1772 internal unsafe String ToLowerInvariant ()
1775 string tmp = InternalAllocateStr (length);
1776 fixed (char* source = &start_char, dest = tmp) {
1778 char* destPtr = (char*)dest;
1779 char* sourcePtr = (char*)source;
1781 for (int n = 0; n < length; n++) {
1782 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1790 public String ToUpper ()
1792 return ToUpper (CultureInfo.CurrentCulture);
1795 public String ToUpper (CultureInfo culture)
1797 if (culture == null)
1798 throw new ArgumentNullException ("culture");
1800 if (culture.LCID == 0x007F) // Invariant
1801 return ToUpperInvariant ();
1803 return culture.TextInfo.ToUpper (this);
1807 public unsafe String ToUpperInvariant ()
1809 internal unsafe String ToUpperInvariant ()
1812 string tmp = InternalAllocateStr (length);
1813 fixed (char* source = &start_char, dest = tmp) {
1815 char* destPtr = (char*)dest;
1816 char* sourcePtr = (char*)source;
1818 for (int n = 0; n < length; n++) {
1819 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1827 public override String ToString ()
1832 public String ToString (IFormatProvider provider)
1837 public static String Format (String format, Object arg0)
1839 return Format (null, format, new Object[] {arg0});
1842 public static String Format (String format, Object arg0, Object arg1)
1844 return Format (null, format, new Object[] {arg0, arg1});
1847 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1849 return Format (null, format, new Object[] {arg0, arg1, arg2});
1852 public static string Format (string format, params object[] args)
1854 return Format (null, format, args);
1857 public static string Format (IFormatProvider provider, string format, params object[] args)
1859 StringBuilder b = FormatHelper (null, provider, format, args);
1860 return b.ToString ();
1863 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1866 throw new ArgumentNullException ("format");
1868 throw new ArgumentNullException ("args");
1870 if (result == null) {
1871 /* Try to approximate the size of result to avoid reallocations */
1875 for (i = 0; i < args.Length; ++i) {
1876 string s = args [i] as string;
1882 if (i == args.Length)
1883 result = new StringBuilder (len + format.length);
1885 result = new StringBuilder ();
1890 while (ptr < format.length) {
1891 char c = format[ptr ++];
1894 result.Append (format, start, ptr - start - 1);
1896 // check for escaped open bracket
1898 if (format[ptr] == '{') {
1909 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1910 if (n >= args.Length)
1911 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1915 object arg = args[n];
1918 ICustomFormatter formatter = null;
1919 if (provider != null)
1920 formatter = provider.GetFormat (typeof (ICustomFormatter))
1921 as ICustomFormatter;
1924 else if (formatter != null)
1925 str = formatter.Format (arg_format, arg, provider);
1926 else if (arg is IFormattable)
1927 str = ((IFormattable)arg).ToString (arg_format, provider);
1929 str = arg.ToString ();
1931 // pad formatted string and append to result
1933 if (width > str.length) {
1934 const char padchar = ' ';
1935 int padlen = width - str.length;
1938 result.Append (str);
1939 result.Append (padchar, padlen);
1942 result.Append (padchar, padlen);
1943 result.Append (str);
1947 result.Append (str);
1951 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1952 result.Append (format, start, ptr - start - 1);
1955 else if (c == '}') {
1956 throw new FormatException ("Input string was not in a correct format.");
1960 if (start < format.length)
1961 result.Append (format, start, format.Length - start);
1966 public unsafe static String Copy (String str)
1969 throw new ArgumentNullException ("str");
1971 int length = str.length;
1973 String tmp = InternalAllocateStr (length);
1975 fixed (char *dest = tmp, src = str) {
1976 CharCopy (dest, src, length);
1982 public static String Concat (Object arg0)
1985 return String.Empty;
1987 return arg0.ToString ();
1990 public unsafe static String Concat (Object arg0, Object arg1)
1994 s1 = (arg0 != null) ? arg0.ToString () : null;
1995 s2 = (arg1 != null) ? arg1.ToString () : null;
1999 return String.Empty;
2002 } else if (s2 == null)
2005 String tmp = InternalAllocateStr (s1.Length + s2.Length);
2006 if (s1.Length != 0) {
2007 fixed (char *dest = tmp, src = s1) {
2008 CharCopy (dest, src, s1.length);
2011 if (s2.Length != 0) {
2012 fixed (char *dest = tmp, src = s2) {
2013 CharCopy (dest + s1.Length, src, s2.length);
2020 public static String Concat (Object arg0, Object arg1, Object arg2)
2026 s1 = arg0.ToString ();
2031 s2 = arg1.ToString ();
2036 s3 = arg2.ToString ();
2038 return Concat (s1, s2, s3);
2041 #if ! BOOTSTRAP_WITH_OLDLIB
2042 [CLSCompliant(false)]
2043 public static String Concat (Object arg0, Object arg1, Object arg2,
2044 Object arg3, __arglist)
2046 string s1, s2, s3, s4;
2051 s1 = arg0.ToString ();
2056 s2 = arg1.ToString ();
2061 s3 = arg2.ToString ();
2063 ArgIterator iter = new ArgIterator (__arglist);
2064 int argCount = iter.GetRemainingCount();
2066 StringBuilder sb = new StringBuilder ();
2068 sb.Append (arg3.ToString ());
2070 for (int i = 0; i < argCount; i++) {
2071 TypedReference typedRef = iter.GetNextArg ();
2072 sb.Append (TypedReference.ToObject (typedRef));
2075 s4 = sb.ToString ();
2077 return Concat (s1, s2, s3, s4);
2081 public unsafe static String Concat (String str0, String str1)
2083 if (str0 == null || str0.Length == 0) {
2084 if (str1 == null || str1.Length == 0)
2085 return String.Empty;
2089 if (str1 == null || str1.Length == 0)
2092 String tmp = InternalAllocateStr (str0.length + str1.length);
2094 fixed (char *dest = tmp, src = str0)
2095 CharCopy (dest, src, str0.length);
2096 fixed (char *dest = tmp, src = str1)
2097 CharCopy (dest + str0.Length, src, str1.length);
2102 public unsafe static String Concat (String str0, String str1, String str2)
2104 if (str0 == null || str0.Length == 0){
2105 if (str1 == null || str1.Length == 0){
2106 if (str2 == null || str2.Length == 0)
2107 return String.Empty;
2110 if (str2 == null || str2.Length == 0)
2113 str0 = String.Empty;
2115 if (str1 == null || str1.Length == 0){
2116 if (str2 == null || str2.Length == 0)
2119 str1 = String.Empty;
2121 if (str2 == null || str2.Length == 0)
2122 str2 = String.Empty;
2126 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2128 if (str0.Length != 0) {
2129 fixed (char *dest = tmp, src = str0) {
2130 CharCopy (dest, src, str0.length);
2133 if (str1.Length != 0) {
2134 fixed (char *dest = tmp, src = str1) {
2135 CharCopy (dest + str0.Length, src, str1.length);
2138 if (str2.Length != 0) {
2139 fixed (char *dest = tmp, src = str2) {
2140 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2147 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2149 if (str0 == null && str1 == null && str2 == null && str3 == null)
2150 return String.Empty;
2153 str0 = String.Empty;
2155 str1 = String.Empty;
2157 str2 = String.Empty;
2159 str3 = String.Empty;
2161 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2163 if (str0.Length != 0) {
2164 fixed (char *dest = tmp, src = str0) {
2165 CharCopy (dest, src, str0.length);
2168 if (str1.Length != 0) {
2169 fixed (char *dest = tmp, src = str1) {
2170 CharCopy (dest + str0.Length, src, str1.length);
2173 if (str2.Length != 0) {
2174 fixed (char *dest = tmp, src = str2) {
2175 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2178 if (str3.Length != 0) {
2179 fixed (char *dest = tmp, src = str3) {
2180 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2187 public static String Concat (params Object[] args)
2190 throw new ArgumentNullException ("args");
2192 int argLen = args.Length;
2194 return String.Empty;
2196 string [] strings = new string [argLen];
2198 for (int i = 0; i < argLen; i++) {
2199 if (args[i] != null) {
2200 strings[i] = args[i].ToString ();
2201 len += strings[i].length;
2205 return String.Empty;
2207 return ConcatInternal (strings, len);
2210 public static String Concat (params String[] values)
2213 throw new ArgumentNullException ("values");
2216 for (int i = 0; i < values.Length; i++) {
2217 String s = values[i];
2222 return String.Empty;
2224 return ConcatInternal (values, len);
2227 private static unsafe String ConcatInternal (String[] values, int length)
2229 String tmp = InternalAllocateStr (length);
2231 fixed (char* dest = tmp) {
2233 for (int i = 0; i < values.Length; i++) {
2234 String source = values[i];
2235 if (source != null) {
2236 fixed (char* src = source) {
2237 CharCopy (dest + pos, src, source.length);
2239 pos += source.Length;
2246 public unsafe String Insert (int startIndex, String value)
2249 throw new ArgumentNullException ("value");
2251 if (startIndex < 0 || startIndex > this.length)
2252 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2254 if (value.Length == 0)
2256 if (this.Length == 0)
2258 String tmp = InternalAllocateStr (this.length + value.length);
2260 fixed (char *dest = tmp, src = this, val = value) {
2262 CharCopy (dst, src, startIndex);
2264 CharCopy (dst, val, value.length);
2265 dst += value.length;
2266 CharCopy (dst, src + startIndex, length - startIndex);
2271 public static string Intern (string str)
2274 throw new ArgumentNullException ("str");
2276 return InternalIntern (str);
2279 public static string IsInterned (string str)
2282 throw new ArgumentNullException ("str");
2284 return InternalIsInterned (str);
2287 public static string Join (string separator, string [] value)
2290 throw new ArgumentNullException ("value");
2291 if (separator == null)
2292 separator = String.Empty;
2294 return JoinUnchecked (separator, value, 0, value.Length);
2297 public static string Join (string separator, string[] value, int startIndex, int count)
2300 throw new ArgumentNullException ("value");
2302 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2304 throw new ArgumentOutOfRangeException ("count", "< 0");
2305 if (startIndex > value.Length - count)
2306 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2308 if (startIndex == value.Length)
2309 return String.Empty;
2310 if (separator == null)
2311 separator = String.Empty;
2313 return JoinUnchecked (separator, value, startIndex, count);
2316 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2318 // Unchecked parameters
2319 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2320 // separator and value must not be null
2323 int maxIndex = startIndex + count;
2324 // Precount the number of characters that the resulting string will have
2325 for (int i = startIndex; i < maxIndex; i++) {
2326 String s = value[i];
2330 length += separator.length * (count - 1);
2332 return String.Empty;
2334 String tmp = InternalAllocateStr (length);
2337 fixed (char* dest = tmp, sepsrc = separator) {
2338 // Copy each string from value except the last one and add a separator for each
2340 for (int i = startIndex; i < maxIndex; i++) {
2341 String source = value[i];
2342 if (source != null) {
2343 if (source.Length > 0) {
2344 fixed (char* src = source)
2345 CharCopy (dest + pos, src, source.Length);
2346 pos += source.Length;
2349 if (separator.Length > 0) {
2350 CharCopy (dest + pos, sepsrc, separator.Length);
2351 pos += separator.Length;
2354 // Append last string that does not get an additional separator
2355 String sourceLast = value[maxIndex];
2356 if (sourceLast != null) {
2357 if (sourceLast.Length > 0) {
2358 fixed (char* src = sourceLast)
2359 CharCopy (dest + pos, src, sourceLast.Length);
2366 bool IConvertible.ToBoolean (IFormatProvider provider)
2368 return Convert.ToBoolean (this, provider);
2371 byte IConvertible.ToByte (IFormatProvider provider)
2373 return Convert.ToByte (this, provider);
2376 char IConvertible.ToChar (IFormatProvider provider)
2378 return Convert.ToChar (this, provider);
2381 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2383 return Convert.ToDateTime (this, provider);
2386 decimal IConvertible.ToDecimal (IFormatProvider provider)
2388 return Convert.ToDecimal (this, provider);
2391 double IConvertible.ToDouble (IFormatProvider provider)
2393 return Convert.ToDouble (this, provider);
2396 short IConvertible.ToInt16 (IFormatProvider provider)
2398 return Convert.ToInt16 (this, provider);
2401 int IConvertible.ToInt32 (IFormatProvider provider)
2403 return Convert.ToInt32 (this, provider);
2406 long IConvertible.ToInt64 (IFormatProvider provider)
2408 return Convert.ToInt64 (this, provider);
2412 #pragma warning disable 3019
2413 [CLSCompliant (false)]
2415 sbyte IConvertible.ToSByte (IFormatProvider provider)
2417 return Convert.ToSByte (this, provider);
2420 #pragma warning restore 3019
2423 float IConvertible.ToSingle (IFormatProvider provider)
2425 return Convert.ToSingle (this, provider);
2428 object IConvertible.ToType (Type type, IFormatProvider provider)
2430 return Convert.ToType (this, type, provider, false);
2434 #pragma warning disable 3019
2435 [CLSCompliant (false)]
2437 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2439 return Convert.ToUInt16 (this, provider);
2442 #pragma warning restore 3019
2446 #pragma warning disable 3019
2447 [CLSCompliant (false)]
2449 uint IConvertible.ToUInt32 (IFormatProvider provider)
2451 return Convert.ToUInt32 (this, provider);
2454 #pragma warning restore 3019
2458 #pragma warning disable 3019
2459 [CLSCompliant (false)]
2461 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2463 return Convert.ToUInt64 (this, provider);
2466 #pragma warning restore 3019
2475 public CharEnumerator GetEnumerator ()
2477 return new CharEnumerator (this);
2481 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2483 return new CharEnumerator (this);
2487 IEnumerator IEnumerable.GetEnumerator ()
2489 return new CharEnumerator (this);
2492 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2493 out bool left_align, out string format)
2495 // parses format specifier of form:
2501 // N = argument number (non-negative integer)
2503 n = ParseDecimal (str, ref ptr);
2505 throw new FormatException ("Input string was not in a correct format.");
2507 // M = width (non-negative integer)
2509 if (str[ptr] == ',') {
2510 // White space between ',' and number or sign.
2512 while (Char.IsWhiteSpace (str [ptr]))
2516 format = str.Substring (start, ptr - start);
2518 left_align = (str [ptr] == '-');
2522 width = ParseDecimal (str, ref ptr);
2524 throw new FormatException ("Input string was not in a correct format.");
2529 format = String.Empty;
2532 // F = argument format (string)
2534 if (str[ptr] == ':') {
2536 while (str[ptr] != '}')
2539 format += str.Substring (start, ptr - start);
2544 if (str[ptr ++] != '}')
2545 throw new FormatException ("Input string was not in a correct format.");
2547 catch (IndexOutOfRangeException) {
2548 throw new FormatException ("Input string was not in a correct format.");
2552 private static int ParseDecimal (string str, ref int ptr)
2558 if (c < '0' || '9' < c)
2561 n = n * 10 + c - '0';
2572 internal unsafe void InternalSetChar (int idx, char val)
2574 if ((uint) idx >= (uint) Length)
2575 throw new ArgumentOutOfRangeException ("idx");
2577 fixed (char * pStr = &start_char)
2583 internal unsafe void InternalSetLength (int newLength)
2585 if (newLength > length)
2586 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2588 // zero terminate, we can pass string objects directly via pinvoke
2589 // we also zero the rest of the string, since the new GC needs to be
2590 // able to handle the changing size (it will skip the 0 bytes).
2591 fixed (char * pStr = &start_char) {
2592 char *p = pStr + newLength;
2593 char *end = pStr + length;
2603 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2605 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2606 public unsafe override int GetHashCode ()
2608 fixed (char * c = this) {
2610 char * end = cc + length - 1;
2612 for (;cc < end; cc += 2) {
2613 h = (h << 5) - h + *cc;
2614 h = (h << 5) - h + cc [1];
2618 h = (h << 5) - h + *cc;
2623 internal unsafe int GetCaseInsensitiveHashCode ()
2625 fixed (char * c = this) {
2627 char * end = cc + length - 1;
2629 for (;cc < end; cc += 2) {
2630 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2631 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2635 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2640 // Certain constructors are redirected to CreateString methods with
2641 // matching argument list. The this pointer should not be used.
2642 #pragma warning disable 169
2643 private unsafe String CreateString (sbyte* value)
2646 return String.Empty;
2648 byte* bytes = (byte*) value;
2652 while (bytes++ [0] != 0)
2654 } catch (NullReferenceException) {
2655 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2657 } catch (AccessViolationException) {
2658 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2662 return CreateString (value, 0, length, null);
2665 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2667 return CreateString (value, startIndex, length, null);
2670 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2673 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2675 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2676 if (value + startIndex < value)
2677 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2679 bool isDefaultEncoding;
2681 if (isDefaultEncoding = (enc == null)) {
2684 throw new ArgumentNullException ("value");
2687 if (value == null || length == 0)
2689 return String.Empty;
2691 enc = Encoding.Default;
2694 byte [] bytes = new byte [length];
2697 fixed (byte* bytePtr = bytes)
2699 memcpy (bytePtr, (byte*) (value + startIndex), length);
2700 } catch (NullReferenceException) {
2702 if (!isDefaultEncoding)
2706 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2708 } catch (AccessViolationException) {
2709 if (!isDefaultEncoding)
2712 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2716 // GetString () is called even when length == 0
2717 return enc.GetString (bytes);
2720 unsafe string CreateString (char *value)
2723 return string.Empty;
2730 string result = InternalAllocateStr (i);
2733 fixed (char *dest = result) {
2734 CharCopy (dest, value, i);
2740 unsafe string CreateString (char *value, int startIndex, int length)
2743 return string.Empty;
2745 throw new ArgumentNullException ("value");
2747 throw new ArgumentOutOfRangeException ("startIndex");
2749 throw new ArgumentOutOfRangeException ("length");
2751 string result = InternalAllocateStr (length);
2753 fixed (char *dest = result) {
2754 CharCopy (dest, value + startIndex, length);
2759 unsafe string CreateString (char [] val, int startIndex, int length)
2762 throw new ArgumentNullException ("value");
2764 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2766 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2767 if (startIndex > val.Length - length)
2768 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2770 return string.Empty;
2772 string result = InternalAllocateStr (length);
2774 fixed (char *dest = result, src = val) {
2775 CharCopy (dest, src + startIndex, length);
2780 unsafe string CreateString (char [] val)
2783 return string.Empty;
2784 if (val.Length == 0)
2785 return string.Empty;
2786 string result = InternalAllocateStr (val.Length);
2788 fixed (char *dest = result, src = val) {
2789 CharCopy (dest, src, val.Length);
2794 unsafe string CreateString (char c, int count)
2797 throw new ArgumentOutOfRangeException ("count");
2799 return string.Empty;
2800 string result = InternalAllocateStr (count);
2801 fixed (char *dest = result) {
2803 char *end = p + count;
2811 #pragma warning restore 169
2813 /* helpers used by the runtime as well as above or eslewhere in corlib */
2814 internal static unsafe void memset (byte *dest, int val, int len)
2825 val = val | (val << 8);
2826 val = val | (val << 16);
2829 int rest = (int)dest & 3;
2837 } while (rest != 0);
2840 ((int*)dest) [0] = val;
2841 ((int*)dest) [1] = val;
2842 ((int*)dest) [2] = val;
2843 ((int*)dest) [3] = val;
2848 ((int*)dest) [0] = val;
2860 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2861 /*while (size >= 32) {
2862 // using long is better than int and slower than double
2863 // FIXME: enable this only on correct alignment or on platforms
2864 // that can tolerate unaligned reads/writes of doubles
2865 ((double*)dest) [0] = ((double*)src) [0];
2866 ((double*)dest) [1] = ((double*)src) [1];
2867 ((double*)dest) [2] = ((double*)src) [2];
2868 ((double*)dest) [3] = ((double*)src) [3];
2873 while (size >= 16) {
2874 ((int*)dest) [0] = ((int*)src) [0];
2875 ((int*)dest) [1] = ((int*)src) [1];
2876 ((int*)dest) [2] = ((int*)src) [2];
2877 ((int*)dest) [3] = ((int*)src) [3];
2883 ((int*)dest) [0] = ((int*)src) [0];
2889 ((byte*)dest) [0] = ((byte*)src) [0];
2895 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2897 ((short*)dest) [0] = ((short*)src) [0];
2898 ((short*)dest) [1] = ((short*)src) [1];
2899 ((short*)dest) [2] = ((short*)src) [2];
2900 ((short*)dest) [3] = ((short*)src) [3];
2906 ((short*)dest) [0] = ((short*)src) [0];
2912 ((byte*)dest) [0] = ((byte*)src) [0];
2914 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2916 ((byte*)dest) [0] = ((byte*)src) [0];
2917 ((byte*)dest) [1] = ((byte*)src) [1];
2918 ((byte*)dest) [2] = ((byte*)src) [2];
2919 ((byte*)dest) [3] = ((byte*)src) [3];
2920 ((byte*)dest) [4] = ((byte*)src) [4];
2921 ((byte*)dest) [5] = ((byte*)src) [5];
2922 ((byte*)dest) [6] = ((byte*)src) [6];
2923 ((byte*)dest) [7] = ((byte*)src) [7];
2929 ((byte*)dest) [0] = ((byte*)src) [0];
2930 ((byte*)dest) [1] = ((byte*)src) [1];
2936 ((byte*)dest) [0] = ((byte*)src) [0];
2939 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2940 // FIXME: if pointers are not aligned, try to align them
2941 // so a faster routine can be used. Handle the case where
2942 // the pointers can't be reduced to have the same alignment
2943 // (just ignore the issue on x86?)
2944 if ((((int)dest | (int)src) & 3) != 0) {
2945 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2951 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2952 ((short*)dest) [0] = ((short*)src) [0];
2957 if ((((int)dest | (int)src) & 1) != 0) {
2958 memcpy1 (dest, src, size);
2961 if ((((int)dest | (int)src) & 2) != 0) {
2962 memcpy2 (dest, src, size);
2966 memcpy4 (dest, src, size);
2969 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2970 // Same rules as for memcpy, but with the premise that
2971 // chars can only be aligned to even addresses if their
2972 // enclosing types are correctly aligned
2973 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2974 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2975 ((short*)dest) [0] = ((short*)src) [0];
2980 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2981 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2985 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2988 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2992 for (int i = count; i > 0; i--) {
2999 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3001 fixed (char* dest = target, src = source)
3002 CharCopy (dest + targetIndex, src + sourceIndex, count);
3005 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3007 fixed (char* dest = target, src = source)
3008 CharCopy (dest + targetIndex, src + sourceIndex, count);
3011 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3012 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3014 fixed (char* dest = target, src = source)
3015 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3018 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3019 unsafe public extern String (char *value);
3021 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3022 unsafe public extern String (char *value, int startIndex, int length);
3024 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3025 unsafe public extern String (sbyte *value);
3027 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3028 unsafe public extern String (sbyte *value, int startIndex, int length);
3030 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3031 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3033 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3034 public extern String (char [] value, int startIndex, int length);
3036 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3037 public extern String (char [] value);
3039 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3040 public extern String (char c, int count);
3042 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3043 // private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
3045 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3046 // private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
3048 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3049 // private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3051 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3052 private extern String[] InternalSplit (char[] separator, int count, int options);
3054 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3055 // private extern String InternalTrim (char[] chars, int typ);
3057 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3058 // private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3060 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3061 // private extern String InternalPad (int width, char chr, bool right);
3063 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3064 internal extern static String InternalAllocateStr (int length);
3066 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3067 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3069 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3070 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3072 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3073 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3075 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3076 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3078 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3079 private extern static string InternalIntern (string str);
3081 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3082 private extern static string InternalIsInterned (string str);