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;
48 using System.Collections.Generic;
49 using System.Runtime.ConstrainedExecution;
50 using System.Runtime.InteropServices;
51 using Mono.Globalization.Unicode;
58 [StructLayout (LayoutKind.Sequential)]
59 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
61 [NonSerialized] private int length;
62 [NonSerialized] private char start_char;
64 public static readonly String Empty = "";
66 internal static readonly int LOS_limit = GetLOSLimit ();
68 public static unsafe bool Equals (string a, string b)
70 if ((a as object) == (b as object))
73 if (a == null || b == null)
81 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
86 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
87 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
88 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
89 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
98 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
99 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
108 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
116 return len == 0 || *s1_ptr == *s2_ptr;
120 public static bool operator == (String a, String b)
122 return Equals (a, b);
125 public static bool operator != (String a, String b)
127 return !Equals (a, b);
130 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
131 public override bool Equals (Object obj)
133 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
137 public bool Equals (String value)
139 return Equals (this, value);
142 [IndexerName ("Chars")]
143 public unsafe char this [int index] {
145 if (index < 0 || index >= length)
146 throw new IndexOutOfRangeException ();
147 fixed (char* c = &start_char)
152 public Object Clone ()
157 public TypeCode GetTypeCode ()
159 return TypeCode.String;
162 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
164 if (destination == null)
165 throw new ArgumentNullException ("destination");
167 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
168 if (destinationIndex < 0)
169 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
171 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
172 if (sourceIndex > Length - count)
173 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
174 if (destinationIndex > destination.Length - count)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
177 fixed (char* dest = destination, src = this)
178 CharCopy (dest + destinationIndex, src + sourceIndex, count);
181 public char[] ToCharArray ()
183 return ToCharArray (0, length);
186 public unsafe char[] ToCharArray (int startIndex, int length)
189 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
191 throw new ArgumentOutOfRangeException ("length", "< 0");
192 if (startIndex > this.length - length)
193 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
195 char[] tmp = new char [length];
196 fixed (char* dest = tmp, src = this)
197 CharCopy (dest, src + startIndex, length);
201 public String [] Split (params char [] separator)
203 return Split (separator, int.MaxValue, 0);
206 public String[] Split (char[] separator, int count)
208 return Split (separator, count, 0);
212 public String[] Split (char[] separator, StringSplitOptions options)
214 return Split (separator, Int32.MaxValue, options);
218 public String[] Split (char[] separator, int count, StringSplitOptions options)
221 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
222 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
223 throw new ArgumentException ("Illegal enum value: " + options + ".");
225 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
226 return new String[0];
231 new String[1] { this };
234 return SplitByCharacters (separator, count, options != 0);
238 public String[] Split (string[] separator, StringSplitOptions options)
240 return Split (separator, Int32.MaxValue, options);
244 public String[] Split (string[] separator, int count, StringSplitOptions options)
247 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
248 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
249 throw new ArgumentException ("Illegal enum value: " + options + ".");
254 new String[1] { this };
257 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
259 if (separator == null || separator.Length == 0)
260 return SplitByCharacters (null, count, removeEmpty);
262 if (Length == 0 && removeEmpty)
263 return new String [0];
265 List<String> arr = new List<String> ();
269 while (pos < this.Length) {
271 int matchPos = Int32.MaxValue;
273 // Find the first position where any of the separators matches
274 for (int i = 0; i < separator.Length; ++i) {
275 string sep = separator [i];
276 if (sep == null || sep.Length == 0)
279 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
280 if (match >= 0 && match < matchPos) {
286 if (matchIndex == -1)
289 if (!(matchPos == pos && removeEmpty)) {
290 if (arr.Count == count - 1)
292 arr.Add (this.Substring (pos, matchPos - pos));
295 pos = matchPos + separator [matchIndex].Length;
301 return new String [] { this };
303 // string contained only separators
304 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
305 return new String [0];
307 if (!(removeEmpty && pos == this.Length))
308 arr.Add (this.Substring (pos));
310 return arr.ToArray ();
313 // .NET 2.0 compatibility only
314 #if !NET_4_0 && !MOONLIGHT && !MOBILE
315 static readonly char[] WhiteChars = {
316 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
317 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
318 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
319 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
320 (char) 0x3000, (char) 0xFEFF
324 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
326 #if !NET_4_0 && !MOONLIGHT && !MOBILE
327 if (sep == null || sep.Length == 0)
331 int[] split_points = null;
332 int total_points = 0;
335 if (sep == null || sep.Length == 0) {
336 fixed (char* src = this) {
341 if (char.IsWhiteSpace (*src_ptr++)) {
342 if (split_points == null) {
343 split_points = new int[8];
344 } else if (split_points.Length == total_points) {
345 Array.Resize (ref split_points, split_points.Length * 2);
348 split_points[total_points++] = Length - len;
349 if (total_points == count && !removeEmpty)
356 fixed (char* src = this) {
357 fixed (char* sep_src = sep) {
359 char* sep_ptr_end = sep_src + sep.Length;
362 char* sep_ptr = sep_src;
364 if (*sep_ptr++ == *src_ptr) {
365 if (split_points == null) {
366 split_points = new int[8];
367 } else if (split_points.Length == total_points) {
368 Array.Resize (ref split_points, split_points.Length * 2);
371 split_points[total_points++] = Length - len;
372 if (total_points == count && !removeEmpty)
377 } while (sep_ptr != sep_ptr_end);
386 if (total_points == 0)
387 return new string[] { this };
389 var res = new string[Math.Min (total_points, count) + 1];
393 for (; i < total_points; ++i) {
394 var start = split_points[i];
395 res[i] = SubstringUnchecked (prev_index, start - prev_index);
396 prev_index = start + 1;
399 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
403 for (; i < total_points; ++i) {
404 var start = split_points[i];
405 length = start - prev_index;
410 res[used++] = SubstringUnchecked (prev_index, length);
413 prev_index = start + 1;
416 length = Length - prev_index;
418 res[used++] = SubstringUnchecked (prev_index, length);
420 if (used != res.Length)
421 Array.Resize (ref res, used);
427 public String Substring (int startIndex)
431 if (startIndex < 0 || startIndex > this.length)
432 throw new ArgumentOutOfRangeException ("startIndex");
434 return SubstringUnchecked (startIndex, this.length - startIndex);
437 public String Substring (int startIndex, int length)
440 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
442 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
443 if (startIndex > this.length)
444 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
445 if (startIndex > this.length - length)
446 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
447 if (startIndex == 0 && length == this.length)
450 return SubstringUnchecked (startIndex, length);
453 // This method is used by StringBuilder.ToString() and is expected to
454 // always create a new string object (or return String.Empty).
455 internal unsafe String SubstringUnchecked (int startIndex, int length)
460 string tmp = InternalAllocateStr (length);
461 fixed (char* dest = tmp, src = this) {
462 CharCopy (dest, src + startIndex, length);
467 public String Trim ()
471 int start = FindNotWhiteSpace (0, length, 1);
476 int end = FindNotWhiteSpace (length - 1, start, -1);
478 int newLength = end - start + 1;
479 if (newLength == length)
482 return SubstringUnchecked (start, newLength);
485 public String Trim (params char[] trimChars)
487 if (trimChars == null || trimChars.Length == 0)
492 int start = FindNotInTable (0, length, 1, trimChars);
497 int end = FindNotInTable (length - 1, start, -1, trimChars);
499 int newLength = end - start + 1;
500 if (newLength == length)
503 return SubstringUnchecked (start, newLength);
506 public String TrimStart (params char[] trimChars)
511 if (trimChars == null || trimChars.Length == 0)
512 start = FindNotWhiteSpace (0, length, 1);
514 start = FindNotInTable (0, length, 1, trimChars);
519 return SubstringUnchecked (start, length - start);
522 public String TrimEnd (params char[] trimChars)
527 if (trimChars == null || trimChars.Length == 0)
528 end = FindNotWhiteSpace (length - 1, -1, -1);
530 end = FindNotInTable (length - 1, -1, -1, trimChars);
536 return SubstringUnchecked (0, end);
539 unsafe int FindNotWhiteSpace (int pos, int target, int change)
541 #if NET_4_0 || NET_2_1
542 fixed (char* src = this) {
543 while (pos != target) {
544 if (!char.IsWhiteSpace (src[pos]))
551 while (pos != target) {
555 if (c < 0x9 || c > 0xD)
560 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
561 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
562 if (c < 0x2000 || c > 0x200B)
572 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
574 fixed (char* tablePtr = table, thisPtr = this) {
575 while (pos != target) {
576 char c = thisPtr[pos];
578 while (x < table.Length) {
579 if (c == tablePtr[x])
583 if (x == table.Length)
591 public static int Compare (String strA, String strB)
593 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
596 public static int Compare (String strA, String strB, bool ignoreCase)
598 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
601 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
604 throw new ArgumentNullException ("culture");
606 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
609 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
611 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
614 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
616 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
619 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
622 throw new ArgumentNullException ("culture");
624 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
625 throw new ArgumentOutOfRangeException ();
637 else if (strB == null) {
641 CompareOptions compopts;
644 compopts = CompareOptions.IgnoreCase;
646 compopts = CompareOptions.None;
648 // Need to cap the requested length to the
649 // length of the string, because
650 // CompareInfo.Compare will insist that length
651 // <= (string.Length - offset)
656 if (length > (strA.Length - indexA)) {
657 len1 = strA.Length - indexA;
660 if (length > (strB.Length - indexB)) {
661 len2 = strB.Length - indexB;
664 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
665 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
668 public static int Compare (string strA, string strB, StringComparison comparisonType)
670 switch (comparisonType) {
671 case StringComparison.CurrentCulture:
672 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
673 case StringComparison.CurrentCultureIgnoreCase:
674 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
675 case StringComparison.InvariantCulture:
676 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
677 case StringComparison.InvariantCultureIgnoreCase:
678 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
679 case StringComparison.Ordinal:
680 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
681 case StringComparison.OrdinalIgnoreCase:
682 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
684 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
685 throw new ArgumentException (msg, "comparisonType");
689 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
691 switch (comparisonType) {
692 case StringComparison.CurrentCulture:
693 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
694 case StringComparison.CurrentCultureIgnoreCase:
695 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
696 case StringComparison.InvariantCulture:
697 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
698 case StringComparison.InvariantCultureIgnoreCase:
699 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
700 case StringComparison.Ordinal:
701 return CompareOrdinal (strA, indexA, strB, indexB, length);
702 case StringComparison.OrdinalIgnoreCase:
703 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
705 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
706 throw new ArgumentException (msg, "comparisonType");
710 public static bool Equals (string a, string b, StringComparison comparisonType)
712 return String.Compare (a, b, comparisonType) == 0;
715 public bool Equals (string value, StringComparison comparisonType)
717 return String.Compare (value, this, comparisonType) == 0;
720 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
723 throw new ArgumentNullException ("culture");
725 return culture.CompareInfo.Compare (strA, strB, options);
728 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
731 throw new ArgumentNullException ("culture");
736 if (length > (strA.Length - indexA))
737 len1 = strA.Length - indexA;
739 if (length > (strB.Length - indexB))
740 len2 = strB.Length - indexB;
742 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
745 public int CompareTo (Object value)
750 if (!(value is String))
751 throw new ArgumentException ();
753 return String.Compare (this, (String) value);
756 public int CompareTo (String strB)
761 return Compare (this, strB);
764 public static int CompareOrdinal (String strA, String strB)
766 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
769 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
771 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
772 throw new ArgumentOutOfRangeException ();
774 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
777 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
779 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
780 throw new ArgumentOutOfRangeException ();
782 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
785 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
792 } else if (strB == null) {
795 int lengthA = Math.Min (lenA, strA.Length - indexA);
796 int lengthB = Math.Min (lenB, strB.Length - indexB);
798 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
801 fixed (char* aptr = strA, bptr = strB) {
802 char* ap = aptr + indexA;
803 char* end = ap + Math.Min (lengthA, lengthB);
804 char* bp = bptr + indexB;
811 return lengthA - lengthB;
815 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
817 // Same as above, but checks versus uppercase characters
823 } else if (strB == null) {
826 int lengthA = Math.Min (lenA, strA.Length - indexA);
827 int lengthB = Math.Min (lenB, strB.Length - indexB);
829 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
832 fixed (char* aptr = strA, bptr = strB) {
833 char* ap = aptr + indexA;
834 char* end = ap + Math.Min (lengthA, lengthB);
835 char* bp = bptr + indexB;
838 char c1 = Char.ToUpperInvariant (*ap);
839 char c2 = Char.ToUpperInvariant (*bp);
846 return lengthA - lengthB;
850 public bool EndsWith (String value)
853 throw new ArgumentNullException ("value");
855 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
858 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
861 throw new ArgumentNullException ("value");
863 culture = CultureInfo.CurrentCulture;
865 return culture.CompareInfo.IsSuffix (this, value,
866 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
869 // Following methods are culture-insensitive
870 public int IndexOfAny (char [] anyOf)
873 throw new ArgumentNullException ();
874 if (this.length == 0)
877 return IndexOfAnyUnchecked (anyOf, 0, this.length);
880 public int IndexOfAny (char [] anyOf, int startIndex)
883 throw new ArgumentNullException ();
884 if (startIndex < 0 || startIndex > this.length)
885 throw new ArgumentOutOfRangeException ();
887 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
890 public int IndexOfAny (char [] anyOf, int startIndex, int count)
893 throw new ArgumentNullException ();
894 if (startIndex < 0 || startIndex > this.length)
895 throw new ArgumentOutOfRangeException ();
896 if (count < 0 || startIndex > this.length - count)
897 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
899 return IndexOfAnyUnchecked (anyOf, startIndex, count);
902 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
904 if (anyOf.Length == 0)
907 if (anyOf.Length == 1)
908 return IndexOfUnchecked (anyOf[0], startIndex, count);
910 fixed (char* any = anyOf) {
914 char* end_any_ptr = any + anyOf.Length;
916 while (++any_ptr != end_any_ptr) {
917 if (*any_ptr > highest) {
922 if (*any_ptr < lowest)
926 fixed (char* start = &start_char) {
927 char* ptr = start + startIndex;
928 char* end_ptr = ptr + count;
930 while (ptr != end_ptr) {
931 if (*ptr > highest || *ptr < lowest) {
937 return (int)(ptr - start);
940 while (++any_ptr != end_any_ptr) {
941 if (*ptr == *any_ptr)
942 return (int)(ptr - start);
953 public int IndexOf (string value, StringComparison comparisonType)
955 return IndexOf (value, 0, this.Length, comparisonType);
958 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
960 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
963 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
965 switch (comparisonType) {
966 case StringComparison.CurrentCulture:
967 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
968 case StringComparison.CurrentCultureIgnoreCase:
969 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
970 case StringComparison.InvariantCulture:
971 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
972 case StringComparison.InvariantCultureIgnoreCase:
973 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
974 case StringComparison.Ordinal:
975 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
976 case StringComparison.OrdinalIgnoreCase:
977 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
979 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
980 throw new ArgumentException (msg, "comparisonType");
984 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
987 throw new ArgumentNullException ("value");
989 throw new ArgumentOutOfRangeException ("startIndex");
990 if (count < 0 || (this.length - startIndex) < count)
991 throw new ArgumentOutOfRangeException ("count");
993 if (options == CompareOptions.Ordinal)
994 return IndexOfOrdinalUnchecked (value, startIndex, count);
995 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
998 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
1000 int valueLen = value.Length;
1001 if (count < valueLen)
1004 if (valueLen <= 1) {
1006 return IndexOfUnchecked (value[0], startIndex, count);
1010 fixed (char* thisptr = this, valueptr = value) {
1011 char* ap = thisptr + startIndex;
1012 char* thisEnd = ap + count - valueLen + 1;
1013 while (ap != thisEnd) {
1014 if (*ap == *valueptr) {
1015 for (int i = 1; i < valueLen; i++) {
1016 if (ap[i] != valueptr[i])
1019 return (int)(ap - thisptr);
1028 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1030 int valueLen = value.Length;
1031 if (count < valueLen)
1037 fixed (char* thisptr = this, valueptr = value) {
1038 char* ap = thisptr + startIndex;
1039 char* thisEnd = ap + count - valueLen + 1;
1040 while (ap != thisEnd) {
1041 for (int i = 0; i < valueLen; i++) {
1042 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1045 return (int)(ap - thisptr);
1053 public int LastIndexOf (string value, StringComparison comparisonType)
1055 if (this.Length == 0)
1056 return value.Length == 0 ? 0 : -1;
1058 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1061 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1063 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1066 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1068 switch (comparisonType) {
1069 case StringComparison.CurrentCulture:
1070 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1071 case StringComparison.CurrentCultureIgnoreCase:
1072 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1073 case StringComparison.InvariantCulture:
1074 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1075 case StringComparison.InvariantCultureIgnoreCase:
1076 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1077 case StringComparison.Ordinal:
1078 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1079 case StringComparison.OrdinalIgnoreCase:
1080 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1082 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1083 throw new ArgumentException (msg, "comparisonType");
1087 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1090 throw new ArgumentNullException ("value");
1091 if (this.Length == 0)
1092 return value.Length == 0 ? 0 : -1;
1093 if (value.Length == 0)
1094 return Math.Min (this.Length - 1, startIndex);
1095 if (startIndex < 0 || startIndex > length)
1096 throw new ArgumentOutOfRangeException ("startIndex");
1097 if (count < 0 || (startIndex < count - 1))
1098 throw new ArgumentOutOfRangeException ("count");
1100 if (options == CompareOptions.Ordinal)
1101 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1102 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1105 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1107 int valueLen = value.Length;
1108 if (count < valueLen)
1111 if (valueLen <= 1) {
1113 return LastIndexOfUnchecked (value[0], startIndex, count);
1117 fixed (char* thisptr = this, valueptr = value) {
1118 char* ap = thisptr + startIndex - valueLen + 1;
1119 char* thisEnd = ap - count + valueLen - 1;
1120 while (ap != thisEnd) {
1121 if (*ap == *valueptr) {
1122 for (int i = 1; i < valueLen; i++) {
1123 if (ap[i] != valueptr[i])
1126 return (int)(ap - thisptr);
1135 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1137 int valueLen = value.Length;
1138 if (count < valueLen)
1144 fixed (char* thisptr = this, valueptr = value) {
1145 char* ap = thisptr + startIndex - valueLen + 1;
1146 char* thisEnd = ap - count + valueLen - 1;
1147 while (ap != thisEnd) {
1148 for (int i = 0; i < valueLen; i++) {
1149 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1152 return (int)(ap - thisptr);
1160 // Following methods are culture-insensitive
1161 public int IndexOf (char value)
1163 if (this.length == 0)
1166 return IndexOfUnchecked (value, 0, this.length);
1169 public int IndexOf (char value, int startIndex)
1172 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1173 if (startIndex > this.length)
1174 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1176 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1179 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1182 public int IndexOf (char value, int startIndex, int count)
1184 if (startIndex < 0 || startIndex > this.length)
1185 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1187 throw new ArgumentOutOfRangeException ("count", "< 0");
1188 if (startIndex > this.length - count)
1189 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1191 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1194 return IndexOfUnchecked (value, startIndex, count);
1197 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1199 // It helps JIT compiler to optimize comparison
1200 int value_32 = (int)value;
1202 fixed (char* start = &start_char) {
1203 char* ptr = start + startIndex;
1204 char* end_ptr = ptr + (count >> 3 << 3);
1206 while (ptr != end_ptr) {
1207 if (*ptr == value_32)
1208 return (int)(ptr - start);
1209 if (ptr[1] == value_32)
1210 return (int)(ptr - start + 1);
1211 if (ptr[2] == value_32)
1212 return (int)(ptr - start + 2);
1213 if (ptr[3] == value_32)
1214 return (int)(ptr - start + 3);
1215 if (ptr[4] == value_32)
1216 return (int)(ptr - start + 4);
1217 if (ptr[5] == value_32)
1218 return (int)(ptr - start + 5);
1219 if (ptr[6] == value_32)
1220 return (int)(ptr - start + 6);
1221 if (ptr[7] == value_32)
1222 return (int)(ptr - start + 7);
1227 end_ptr += count & 0x07;
1228 while (ptr != end_ptr) {
1229 if (*ptr == value_32)
1230 return (int)(ptr - start);
1238 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1242 int end = startIndex + count;
1243 char c = Char.ToUpperInvariant (value);
1244 fixed (char* s = &start_char) {
1245 for (int i = startIndex; i < end; i++)
1246 if (Char.ToUpperInvariant (s [i]) == c)
1252 // Following methods are culture-sensitive
1253 public int IndexOf (String value)
1256 throw new ArgumentNullException ("value");
1257 if (value.length == 0)
1259 if (this.length == 0)
1261 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.Ordinal);
1264 public int IndexOf (String value, int startIndex)
1266 return IndexOf (value, startIndex, this.length - startIndex);
1269 public int IndexOf (String value, int startIndex, int count)
1272 throw new ArgumentNullException ("value");
1273 if (startIndex < 0 || startIndex > this.length)
1274 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1275 if (count < 0 || startIndex > this.length - count)
1276 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1278 if (value.length == 0)
1281 if (startIndex == 0 && this.length == 0)
1287 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1290 // Following methods are culture-insensitive
1291 public int LastIndexOfAny (char [] anyOf)
1294 throw new ArgumentNullException ();
1295 if (this.length == 0)
1298 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1301 public int LastIndexOfAny (char [] anyOf, int startIndex)
1304 throw new ArgumentNullException ();
1305 if (this.length == 0)
1308 if (startIndex < 0 || startIndex >= this.length)
1309 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1311 if (this.length == 0)
1314 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1317 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1320 throw new ArgumentNullException ();
1321 if (this.length == 0)
1324 if ((startIndex < 0) || (startIndex >= this.Length))
1325 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1326 if ((count < 0) || (count > this.Length))
1327 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1328 if (startIndex - count + 1 < 0)
1329 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1331 if (this.length == 0)
1334 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1337 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1339 if (anyOf.Length == 1)
1340 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1342 fixed (char* start = this, testStart = anyOf) {
1343 char* ptr = start + startIndex;
1344 char* ptrEnd = ptr - count;
1346 char* testEnd = testStart + anyOf.Length;
1348 while (ptr != ptrEnd) {
1350 while (test != testEnd) {
1352 return (int)(ptr - start);
1361 // Following methods are culture-insensitive
1362 public int LastIndexOf (char value)
1364 if (this.length == 0)
1367 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1370 public int LastIndexOf (char value, int startIndex)
1372 return LastIndexOf (value, startIndex, startIndex + 1);
1375 public int LastIndexOf (char value, int startIndex, int count)
1377 if (this.length == 0)
1380 // >= for char (> for string)
1381 if ((startIndex < 0) || (startIndex >= this.Length))
1382 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1383 if ((count < 0) || (count > this.Length))
1384 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1385 if (startIndex - count + 1 < 0)
1386 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1388 return LastIndexOfUnchecked (value, startIndex, count);
1391 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1393 // It helps JIT compiler to optimize comparison
1394 int value_32 = (int)value;
1396 fixed (char* start = &start_char) {
1397 char* ptr = start + startIndex;
1398 char* end_ptr = ptr - (count >> 3 << 3);
1400 while (ptr != end_ptr) {
1401 if (*ptr == value_32)
1402 return (int)(ptr - start);
1403 if (ptr[-1] == value_32)
1404 return (int)(ptr - start) - 1;
1405 if (ptr[-2] == value_32)
1406 return (int)(ptr - start) - 2;
1407 if (ptr[-3] == value_32)
1408 return (int)(ptr - start) - 3;
1409 if (ptr[-4] == value_32)
1410 return (int)(ptr - start) - 4;
1411 if (ptr[-5] == value_32)
1412 return (int)(ptr - start) - 5;
1413 if (ptr[-6] == value_32)
1414 return (int)(ptr - start) - 6;
1415 if (ptr[-7] == value_32)
1416 return (int)(ptr - start) - 7;
1421 end_ptr -= count & 0x07;
1422 while (ptr != end_ptr) {
1423 if (*ptr == value_32)
1424 return (int)(ptr - start);
1432 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1436 int end = startIndex - count;
1437 char c = Char.ToUpperInvariant (value);
1438 fixed (char* s = &start_char) {
1439 for (int i = startIndex; i > end; i--)
1440 if (Char.ToUpperInvariant (s [i]) == c)
1446 // Following methods are culture-sensitive
1447 public int LastIndexOf (String value)
1449 return LastIndexOf (value, this.length - 1, this.length);
1452 public int LastIndexOf (String value, int startIndex)
1454 int max = startIndex;
1455 if (max < this.Length)
1457 return LastIndexOf (value, startIndex, max);
1460 public int LastIndexOf (String value, int startIndex, int count)
1463 throw new ArgumentNullException ("value");
1465 if (this.length == 0)
1466 return value.Length == 0 ? 0 : -1;
1467 // -1 > startIndex > for string (0 > startIndex >= for char)
1468 if ((startIndex < -1) || (startIndex > this.Length))
1469 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1470 if ((count < 0) || (count > this.Length))
1471 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1472 if (startIndex - count + 1 < 0)
1473 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1475 if (value.Length == 0)
1476 return Math.Min (this.Length - 1, startIndex);
1478 if (startIndex == 0 && this.length == 0)
1481 // This check is needed to match undocumented MS behaviour
1482 if (this.length == 0 && value.length > 0)
1488 if (startIndex == this.Length)
1490 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1493 public bool Contains (String value)
1495 return IndexOf (value) != -1;
1498 public static bool IsNullOrEmpty (String value)
1500 return (value == null) || (value.Length == 0);
1504 public string Normalize ()
1506 return Normalization.Normalize (this, 0);
1509 public string Normalize (NormalizationForm normalizationForm)
1511 switch (normalizationForm) {
1513 return Normalization.Normalize (this, 0);
1514 case NormalizationForm.FormD:
1515 return Normalization.Normalize (this, 1);
1516 case NormalizationForm.FormKC:
1517 return Normalization.Normalize (this, 2);
1518 case NormalizationForm.FormKD:
1519 return Normalization.Normalize (this, 3);
1523 public bool IsNormalized ()
1525 return Normalization.IsNormalized (this, 0);
1528 public bool IsNormalized (NormalizationForm normalizationForm)
1530 switch (normalizationForm) {
1532 return Normalization.IsNormalized (this, 0);
1533 case NormalizationForm.FormD:
1534 return Normalization.IsNormalized (this, 1);
1535 case NormalizationForm.FormKC:
1536 return Normalization.IsNormalized (this, 2);
1537 case NormalizationForm.FormKD:
1538 return Normalization.IsNormalized (this, 3);
1543 public string Remove (int startIndex)
1546 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1547 if (startIndex >= this.length)
1548 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1550 return Remove (startIndex, this.length - startIndex);
1553 public String PadLeft (int totalWidth)
1555 return PadLeft (totalWidth, ' ');
1558 public unsafe String PadLeft (int totalWidth, char paddingChar)
1560 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1563 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1565 if (totalWidth < this.length)
1567 if (totalWidth == 0)
1570 String tmp = InternalAllocateStr (totalWidth);
1572 fixed (char* dest = tmp, src = this) {
1573 char* padPos = dest;
1574 char* padTo = dest + (totalWidth - length);
1575 while (padPos != padTo)
1576 *padPos++ = paddingChar;
1578 CharCopy (padTo, src, length);
1583 public String PadRight (int totalWidth)
1585 return PadRight (totalWidth, ' ');
1588 public unsafe String PadRight (int totalWidth, char paddingChar)
1590 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1593 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1595 if (totalWidth < this.length)
1597 if (totalWidth == 0)
1600 String tmp = InternalAllocateStr (totalWidth);
1602 fixed (char* dest = tmp, src = this) {
1603 CharCopy (dest, src, length);
1605 char* padPos = dest + length;
1606 char* padTo = dest + totalWidth;
1607 while (padPos != padTo)
1608 *padPos++ = paddingChar;
1613 public bool StartsWith (String value)
1616 throw new ArgumentNullException ("value");
1618 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1621 [ComVisible (false)]
1622 public bool StartsWith (string value, StringComparison comparisonType)
1625 throw new ArgumentNullException ("value");
1627 switch (comparisonType) {
1628 case StringComparison.CurrentCulture:
1629 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1630 case StringComparison.CurrentCultureIgnoreCase:
1631 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1632 case StringComparison.InvariantCulture:
1633 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1634 case StringComparison.InvariantCultureIgnoreCase:
1635 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1636 case StringComparison.Ordinal:
1637 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1638 case StringComparison.OrdinalIgnoreCase:
1639 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1641 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1642 throw new ArgumentException (msg, "comparisonType");
1646 [ComVisible (false)]
1647 public bool EndsWith (string value, StringComparison comparisonType)
1650 throw new ArgumentNullException ("value");
1652 switch (comparisonType) {
1653 case StringComparison.CurrentCulture:
1654 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1655 case StringComparison.CurrentCultureIgnoreCase:
1656 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1657 case StringComparison.InvariantCulture:
1658 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1659 case StringComparison.InvariantCultureIgnoreCase:
1660 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1661 case StringComparison.Ordinal:
1662 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1663 case StringComparison.OrdinalIgnoreCase:
1664 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1666 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1667 throw new ArgumentException (msg, "comparisonType");
1671 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1673 if (culture == null)
1674 culture = CultureInfo.CurrentCulture;
1676 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1679 // Following method is culture-insensitive
1680 public unsafe String Replace (char oldChar, char newChar)
1682 if (this.length == 0 || oldChar == newChar)
1685 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1686 if (start_pos == -1)
1692 string tmp = InternalAllocateStr (length);
1693 fixed (char* dest = tmp, src = &start_char) {
1695 CharCopy (dest, src, start_pos);
1697 char* end_ptr = dest + length;
1698 char* dest_ptr = dest + start_pos;
1699 char* src_ptr = src + start_pos;
1701 while (dest_ptr != end_ptr) {
1702 if (*src_ptr == oldChar)
1703 *dest_ptr = newChar;
1705 *dest_ptr = *src_ptr;
1714 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1715 public String Replace (String oldValue, String newValue)
1717 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1718 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1720 if (oldValue == null)
1721 throw new ArgumentNullException ("oldValue");
1723 if (oldValue.Length == 0)
1724 throw new ArgumentException ("oldValue is the empty string.");
1726 if (this.Length == 0)
1729 if (newValue == null)
1732 return ReplaceUnchecked (oldValue, newValue);
1735 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1737 if (oldValue.length > length)
1739 if (oldValue.length == 1 && newValue.length == 1) {
1740 return Replace (oldValue[0], newValue[0]);
1741 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1742 // because the length of the result would be this.length and length calculation unneccesary
1745 const int maxValue = 200; // Allocate 800 byte maximum
1746 int* dat = stackalloc int[maxValue];
1747 fixed (char* source = this, replace = newValue) {
1748 int i = 0, count = 0;
1749 while (i < length) {
1750 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1754 if (count < maxValue)
1755 dat[count++] = found;
1757 return ReplaceFallback (oldValue, newValue, maxValue);
1759 i = found + oldValue.length;
1763 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1764 String tmp = InternalAllocateStr (nlen);
1766 int curPos = 0, lastReadPos = 0;
1767 fixed (char* dest = tmp) {
1768 for (int j = 0; j < count; j++) {
1769 int precopy = dat[j] - lastReadPos;
1770 CharCopy (dest + curPos, source + lastReadPos, precopy);
1772 lastReadPos = dat[j] + oldValue.length;
1773 CharCopy (dest + curPos, replace, newValue.length);
1774 curPos += newValue.length;
1776 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1782 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1784 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1785 StringBuilder sb = new StringBuilder (lengthEstimate);
1786 for (int i = 0; i < length;) {
1787 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1789 sb.Append (SubstringUnchecked (i, length - i));
1792 sb.Append (SubstringUnchecked (i, found - i));
1793 sb.Append (newValue);
1794 i = found + oldValue.Length;
1796 return sb.ToString ();
1800 public unsafe String Remove (int startIndex, int count)
1803 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1805 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1806 if (startIndex > this.length - count)
1807 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1809 String tmp = InternalAllocateStr (this.length - count);
1811 fixed (char *dest = tmp, src = this) {
1813 CharCopy (dst, src, startIndex);
1814 int skip = startIndex + count;
1816 CharCopy (dst, src + skip, length - skip);
1821 public String ToLower ()
1823 return ToLower (CultureInfo.CurrentCulture);
1826 public String ToLower (CultureInfo culture)
1828 if (culture == null)
1829 throw new ArgumentNullException ("culture");
1831 if (culture.LCID == 0x007F) // Invariant
1832 return ToLowerInvariant ();
1834 return culture.TextInfo.ToLower (this);
1837 public unsafe String ToLowerInvariant ()
1842 string tmp = InternalAllocateStr (length);
1843 fixed (char* source = &start_char, dest = tmp) {
1845 char* destPtr = (char*)dest;
1846 char* sourcePtr = (char*)source;
1848 for (int n = 0; n < length; n++) {
1849 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1857 public String ToUpper ()
1859 return ToUpper (CultureInfo.CurrentCulture);
1862 public String ToUpper (CultureInfo culture)
1864 if (culture == null)
1865 throw new ArgumentNullException ("culture");
1867 if (culture.LCID == 0x007F) // Invariant
1868 return ToUpperInvariant ();
1870 return culture.TextInfo.ToUpper (this);
1873 public unsafe String ToUpperInvariant ()
1878 string tmp = InternalAllocateStr (length);
1879 fixed (char* source = &start_char, dest = tmp) {
1881 char* destPtr = (char*)dest;
1882 char* sourcePtr = (char*)source;
1884 for (int n = 0; n < length; n++) {
1885 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1893 public override String ToString ()
1898 public String ToString (IFormatProvider provider)
1903 public static String Format (String format, Object arg0)
1905 return Format (null, format, new Object[] {arg0});
1908 public static String Format (String format, Object arg0, Object arg1)
1910 return Format (null, format, new Object[] {arg0, arg1});
1913 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1915 return Format (null, format, new Object[] {arg0, arg1, arg2});
1918 public static string Format (string format, params object[] args)
1920 return Format (null, format, args);
1923 public static string Format (IFormatProvider provider, string format, params object[] args)
1925 StringBuilder b = FormatHelper (null, provider, format, args);
1926 return b.ToString ();
1929 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1932 throw new ArgumentNullException ("format");
1934 throw new ArgumentNullException ("args");
1936 if (result == null) {
1937 /* Try to approximate the size of result to avoid reallocations */
1941 for (i = 0; i < args.Length; ++i) {
1942 string s = args [i] as string;
1948 if (i == args.Length)
1949 result = new StringBuilder (len + format.length);
1951 result = new StringBuilder ();
1956 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1958 while (ptr < format.length) {
1959 char c = format[ptr ++];
1962 result.Append (format, start, ptr - start - 1);
1964 // check for escaped open bracket
1966 if (format[ptr] == '{') {
1977 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1978 if (n >= args.Length)
1979 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1983 object arg = args[n];
1988 else if (formatter != null)
1989 str = formatter.Format (arg_format, arg, provider);
1994 if (arg is IFormattable)
1995 str = ((IFormattable)arg).ToString (arg_format, provider);
1997 str = arg.ToString ();
2000 // pad formatted string and append to result
2001 if (width > str.length) {
2002 const char padchar = ' ';
2003 int padlen = width - str.length;
2006 result.Append (str);
2007 result.Append (padchar, padlen);
2010 result.Append (padchar, padlen);
2011 result.Append (str);
2014 result.Append (str);
2019 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2020 result.Append (format, start, ptr - start - 1);
2023 else if (c == '}') {
2024 throw new FormatException ("Input string was not in a correct format.");
2028 if (start < format.length)
2029 result.Append (format, start, format.Length - start);
2034 public unsafe static String Copy (String str)
2037 throw new ArgumentNullException ("str");
2039 int length = str.length;
2041 String tmp = InternalAllocateStr (length);
2043 fixed (char *dest = tmp, src = str) {
2044 CharCopy (dest, src, length);
2050 public static String Concat (Object arg0)
2055 return arg0.ToString ();
2058 public static String Concat (Object arg0, Object arg1)
2060 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2063 public static String Concat (Object arg0, Object arg1, Object arg2)
2069 s1 = arg0.ToString ();
2074 s2 = arg1.ToString ();
2079 s3 = arg2.ToString ();
2081 return Concat (s1, s2, s3);
2084 [CLSCompliant(false)]
2085 public static String Concat (Object arg0, Object arg1, Object arg2,
2086 Object arg3, __arglist)
2088 string s1, s2, s3, s4;
2093 s1 = arg0.ToString ();
2098 s2 = arg1.ToString ();
2103 s3 = arg2.ToString ();
2105 ArgIterator iter = new ArgIterator (__arglist);
2106 int argCount = iter.GetRemainingCount();
2108 StringBuilder sb = new StringBuilder ();
2110 sb.Append (arg3.ToString ());
2112 for (int i = 0; i < argCount; i++) {
2113 TypedReference typedRef = iter.GetNextArg ();
2114 sb.Append (TypedReference.ToObject (typedRef));
2117 s4 = sb.ToString ();
2119 return Concat (s1, s2, s3, s4);
2122 public unsafe static String Concat (String str0, String str1)
2124 if (str0 == null || str0.Length == 0) {
2125 if (str1 == null || str1.Length == 0)
2130 if (str1 == null || str1.Length == 0)
2133 String tmp = InternalAllocateStr (str0.length + str1.length);
2135 fixed (char *dest = tmp, src = str0)
2136 CharCopy (dest, src, str0.length);
2137 fixed (char *dest = tmp, src = str1)
2138 CharCopy (dest + str0.Length, src, str1.length);
2143 public unsafe static String Concat (String str0, String str1, String str2)
2145 if (str0 == null || str0.Length == 0){
2146 if (str1 == null || str1.Length == 0){
2147 if (str2 == null || str2.Length == 0)
2151 if (str2 == null || str2.Length == 0)
2156 if (str1 == null || str1.Length == 0){
2157 if (str2 == null || str2.Length == 0)
2162 if (str2 == null || str2.Length == 0)
2167 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2169 if (str0.Length != 0) {
2170 fixed (char *dest = tmp, src = str0) {
2171 CharCopy (dest, src, str0.length);
2174 if (str1.Length != 0) {
2175 fixed (char *dest = tmp, src = str1) {
2176 CharCopy (dest + str0.Length, src, str1.length);
2179 if (str2.Length != 0) {
2180 fixed (char *dest = tmp, src = str2) {
2181 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2188 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2190 if (str0 == null && str1 == null && str2 == null && str3 == null)
2202 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2204 if (str0.Length != 0) {
2205 fixed (char *dest = tmp, src = str0) {
2206 CharCopy (dest, src, str0.length);
2209 if (str1.Length != 0) {
2210 fixed (char *dest = tmp, src = str1) {
2211 CharCopy (dest + str0.Length, src, str1.length);
2214 if (str2.Length != 0) {
2215 fixed (char *dest = tmp, src = str2) {
2216 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2219 if (str3.Length != 0) {
2220 fixed (char *dest = tmp, src = str3) {
2221 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2228 public static String Concat (params Object[] args)
2231 throw new ArgumentNullException ("args");
2233 int argLen = args.Length;
2237 string [] strings = new string [argLen];
2239 for (int i = 0; i < argLen; i++) {
2240 if (args[i] != null) {
2241 strings[i] = args[i].ToString ();
2242 len += strings[i].length;
2246 return ConcatInternal (strings, len);
2249 public static String Concat (params String[] values)
2252 throw new ArgumentNullException ("values");
2255 for (int i = 0; i < values.Length; i++) {
2256 String s = values[i];
2261 return ConcatInternal (values, len);
2264 private static unsafe String ConcatInternal (String[] values, int length)
2269 String tmp = InternalAllocateStr (length);
2271 fixed (char* dest = tmp) {
2273 for (int i = 0; i < values.Length; i++) {
2274 String source = values[i];
2275 if (source != null) {
2276 fixed (char* src = source) {
2277 CharCopy (dest + pos, src, source.length);
2279 pos += source.Length;
2286 public unsafe String Insert (int startIndex, String value)
2289 throw new ArgumentNullException ("value");
2291 if (startIndex < 0 || startIndex > this.length)
2292 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2294 if (value.Length == 0)
2296 if (this.Length == 0)
2298 String tmp = InternalAllocateStr (this.length + value.length);
2300 fixed (char *dest = tmp, src = this, val = value) {
2302 CharCopy (dst, src, startIndex);
2304 CharCopy (dst, val, value.length);
2305 dst += value.length;
2306 CharCopy (dst, src + startIndex, length - startIndex);
2311 public static string Intern (string str)
2314 throw new ArgumentNullException ("str");
2316 return InternalIntern (str);
2319 public static string IsInterned (string str)
2322 throw new ArgumentNullException ("str");
2324 return InternalIsInterned (str);
2327 #if NET_4_0 || MOONLIGHT || MOBILE
2328 public static string Join (string separator, params string [] value)
2330 public static string Join (string separator, string [] value)
2334 throw new ArgumentNullException ("value");
2335 if (separator == null)
2338 return JoinUnchecked (separator, value, 0, value.Length);
2341 public static string Join (string separator, string[] value, int startIndex, int count)
2344 throw new ArgumentNullException ("value");
2346 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2348 throw new ArgumentOutOfRangeException ("count", "< 0");
2349 if (startIndex > value.Length - count)
2350 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2352 if (startIndex == value.Length)
2354 if (separator == null)
2357 return JoinUnchecked (separator, value, startIndex, count);
2360 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2362 // Unchecked parameters
2363 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2364 // separator and value must not be null
2367 int maxIndex = startIndex + count;
2368 // Precount the number of characters that the resulting string will have
2369 for (int i = startIndex; i < maxIndex; i++) {
2370 String s = value[i];
2374 length += separator.length * (count - 1);
2378 String tmp = InternalAllocateStr (length);
2381 fixed (char* dest = tmp, sepsrc = separator) {
2382 // Copy each string from value except the last one and add a separator for each
2384 for (int i = startIndex; i < maxIndex; i++) {
2385 String source = value[i];
2386 if (source != null) {
2387 if (source.Length > 0) {
2388 fixed (char* src = source)
2389 CharCopy (dest + pos, src, source.Length);
2390 pos += source.Length;
2393 if (separator.Length > 0) {
2394 CharCopy (dest + pos, sepsrc, separator.Length);
2395 pos += separator.Length;
2398 // Append last string that does not get an additional separator
2399 String sourceLast = value[maxIndex];
2400 if (sourceLast != null) {
2401 if (sourceLast.Length > 0) {
2402 fixed (char* src = sourceLast)
2403 CharCopy (dest + pos, src, sourceLast.Length);
2410 bool IConvertible.ToBoolean (IFormatProvider provider)
2412 return Convert.ToBoolean (this, provider);
2415 byte IConvertible.ToByte (IFormatProvider provider)
2417 return Convert.ToByte (this, provider);
2420 char IConvertible.ToChar (IFormatProvider provider)
2422 return Convert.ToChar (this, provider);
2425 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2427 return Convert.ToDateTime (this, provider);
2430 decimal IConvertible.ToDecimal (IFormatProvider provider)
2432 return Convert.ToDecimal (this, provider);
2435 double IConvertible.ToDouble (IFormatProvider provider)
2437 return Convert.ToDouble (this, provider);
2440 short IConvertible.ToInt16 (IFormatProvider provider)
2442 return Convert.ToInt16 (this, provider);
2445 int IConvertible.ToInt32 (IFormatProvider provider)
2447 return Convert.ToInt32 (this, provider);
2450 long IConvertible.ToInt64 (IFormatProvider provider)
2452 return Convert.ToInt64 (this, provider);
2455 sbyte IConvertible.ToSByte (IFormatProvider provider)
2457 return Convert.ToSByte (this, provider);
2460 float IConvertible.ToSingle (IFormatProvider provider)
2462 return Convert.ToSingle (this, provider);
2465 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2467 if (targetType == null)
2468 throw new ArgumentNullException ("type");
2469 return Convert.ToType (this, targetType, provider, false);
2472 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2474 return Convert.ToUInt16 (this, provider);
2477 uint IConvertible.ToUInt32 (IFormatProvider provider)
2479 return Convert.ToUInt32 (this, provider);
2482 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2484 return Convert.ToUInt64 (this, provider);
2493 public CharEnumerator GetEnumerator ()
2495 return new CharEnumerator (this);
2498 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2500 return new CharEnumerator (this);
2503 IEnumerator IEnumerable.GetEnumerator ()
2505 return new CharEnumerator (this);
2508 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2509 out bool left_align, out string format)
2511 int max = str.Length;
2513 // parses format specifier of form:
2517 // N = argument number (non-negative integer)
2519 n = ParseDecimal (str, ref ptr);
2521 throw new FormatException ("Input string was not in a correct format.");
2523 // M = width (non-negative integer)
2525 if (ptr < max && str[ptr] == ',') {
2526 // White space between ',' and number or sign.
2528 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2532 format = str.Substring (start, ptr - start);
2534 left_align = (ptr < max && str [ptr] == '-');
2538 width = ParseDecimal (str, ref ptr);
2540 throw new FormatException ("Input string was not in a correct format.");
2548 // F = argument format (string)
2550 if (ptr < max && str[ptr] == ':') {
2552 while (ptr < max && str[ptr] != '}')
2555 format += str.Substring (start, ptr - start);
2560 if ((ptr >= max) || str[ptr ++] != '}')
2561 throw new FormatException ("Input string was not in a correct format.");
2564 private static int ParseDecimal (string str, ref int ptr)
2568 int max = str.Length;
2572 if (c < '0' || '9' < c)
2575 n = n * 10 + c - '0';
2579 if (p == ptr || p == max)
2586 internal unsafe void InternalSetChar (int idx, char val)
2588 if ((uint) idx >= (uint) Length)
2589 throw new ArgumentOutOfRangeException ("idx");
2591 fixed (char * pStr = &start_char)
2597 internal unsafe void InternalSetLength (int newLength)
2599 if (newLength > length)
2600 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2602 // zero terminate, we can pass string objects directly via pinvoke
2603 // we also zero the rest of the string, since the new GC needs to be
2604 // able to handle the changing size (it will skip the 0 bytes).
2605 fixed (char * pStr = &start_char) {
2606 char *p = pStr + newLength;
2607 char *end = pStr + length;
2616 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2617 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2618 public unsafe override int GetHashCode ()
2620 fixed (char * c = this) {
2622 char * end = cc + length - 1;
2624 for (;cc < end; cc += 2) {
2625 h = (h << 5) - h + *cc;
2626 h = (h << 5) - h + cc [1];
2630 h = (h << 5) - h + *cc;
2635 #if MOONLIGHT || MOBILE || NET_4_0
2637 public static string Concat (IEnumerable<string> values)
2640 throw new ArgumentNullException ("values");
2642 var stringList = new List<string> ();
2644 foreach (var v in values){
2650 return ConcatInternal (stringList.ToArray (), len);
2653 [ComVisibleAttribute(false)]
2654 public static string Concat<T> (IEnumerable<T> values)
2657 throw new ArgumentNullException ("values");
2659 var stringList = new List<string> ();
2661 foreach (var v in values){
2662 string sr = v.ToString ();
2664 stringList.Add (sr);
2666 return ConcatInternal (stringList.ToArray (), len);
2669 [ComVisibleAttribute(false)]
2670 public static string Join (string separator, IEnumerable<string> values)
2672 if (separator == null)
2673 return Concat (values);
2676 throw new ArgumentNullException ("values");
2678 var stringList = new List<string> ();
2679 foreach (var v in values)
2682 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2685 [ComVisibleAttribute(false)]
2686 public static string Join (string separator, params object [] values)
2688 if (separator == null)
2689 return Concat (values);
2692 throw new ArgumentNullException ("values");
2694 var strCopy = new string [values.Length];
2696 foreach (var v in values)
2697 strCopy [i++] = v.ToString ();
2699 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2702 [ComVisible (false)]
2703 public static string Join<T> (string separator, IEnumerable<T> values)
2705 if (separator == null)
2706 return Concat<T> (values);
2709 throw new ArgumentNullException ("values");
2711 var stringList = new List<string> ();
2712 foreach (var v in values)
2713 stringList.Add (v.ToString ());
2715 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2718 public static bool IsNullOrWhiteSpace (string value)
2720 internal static bool IsNullOrWhiteSpace (string value)
2723 if ((value == null) || (value.Length == 0))
2725 foreach (char c in value)
2726 if (!Char.IsWhiteSpace (c))
2731 internal unsafe int GetCaseInsensitiveHashCode ()
2733 fixed (char * c = this) {
2735 char * end = cc + length - 1;
2737 for (;cc < end; cc += 2) {
2738 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2739 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2743 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2748 // Certain constructors are redirected to CreateString methods with
2749 // matching argument list. The this pointer should not be used.
2751 private unsafe String CreateString (sbyte* value)
2756 byte* bytes = (byte*) value;
2760 while (bytes++ [0] != 0)
2762 } catch (NullReferenceException) {
2763 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2764 } catch (AccessViolationException) {
2765 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2768 return CreateString (value, 0, length, null);
2771 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2773 return CreateString (value, startIndex, length, null);
2776 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2779 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2781 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2782 if (value + startIndex < value)
2783 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2785 bool isDefaultEncoding;
2787 if (isDefaultEncoding = (enc == null)) {
2789 throw new ArgumentNullException ("value");
2793 enc = Encoding.Default;
2796 byte [] bytes = new byte [length];
2799 fixed (byte* bytePtr = bytes)
2801 memcpy (bytePtr, (byte*) (value + startIndex), length);
2802 } catch (NullReferenceException) {
2803 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2804 } catch (AccessViolationException) {
2805 if (!isDefaultEncoding)
2808 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2811 // GetString () is called even when length == 0
2812 return enc.GetString (bytes);
2815 unsafe string CreateString (char *value)
2825 string result = InternalAllocateStr (i);
2828 fixed (char *dest = result) {
2829 CharCopy (dest, value, i);
2835 unsafe string CreateString (char *value, int startIndex, int length)
2840 throw new ArgumentNullException ("value");
2842 throw new ArgumentOutOfRangeException ("startIndex");
2844 throw new ArgumentOutOfRangeException ("length");
2846 string result = InternalAllocateStr (length);
2848 fixed (char *dest = result) {
2849 CharCopy (dest, value + startIndex, length);
2854 unsafe string CreateString (char [] val, int startIndex, int length)
2857 throw new ArgumentNullException ("value");
2859 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2861 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2862 if (startIndex > val.Length - length)
2863 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2867 string result = InternalAllocateStr (length);
2869 fixed (char *dest = result, src = val) {
2870 CharCopy (dest, src + startIndex, length);
2875 unsafe string CreateString (char [] val)
2877 if (val == null || val.Length == 0)
2879 string result = InternalAllocateStr (val.Length);
2881 fixed (char *dest = result, src = val) {
2882 CharCopy (dest, src, val.Length);
2887 unsafe string CreateString (char c, int count)
2890 throw new ArgumentOutOfRangeException ("count");
2893 string result = InternalAllocateStr (count);
2894 fixed (char *dest = result) {
2896 char *end = p + count;
2905 /* helpers used by the runtime as well as above or eslewhere in corlib */
2906 internal static unsafe void memset (byte *dest, int val, int len)
2917 val = val | (val << 8);
2918 val = val | (val << 16);
2921 int rest = (int)dest & 3;
2929 } while (rest != 0);
2932 ((int*)dest) [0] = val;
2933 ((int*)dest) [1] = val;
2934 ((int*)dest) [2] = val;
2935 ((int*)dest) [3] = val;
2940 ((int*)dest) [0] = val;
2952 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2953 /*while (size >= 32) {
2954 // using long is better than int and slower than double
2955 // FIXME: enable this only on correct alignment or on platforms
2956 // that can tolerate unaligned reads/writes of doubles
2957 ((double*)dest) [0] = ((double*)src) [0];
2958 ((double*)dest) [1] = ((double*)src) [1];
2959 ((double*)dest) [2] = ((double*)src) [2];
2960 ((double*)dest) [3] = ((double*)src) [3];
2965 while (size >= 16) {
2966 ((int*)dest) [0] = ((int*)src) [0];
2967 ((int*)dest) [1] = ((int*)src) [1];
2968 ((int*)dest) [2] = ((int*)src) [2];
2969 ((int*)dest) [3] = ((int*)src) [3];
2975 ((int*)dest) [0] = ((int*)src) [0];
2981 ((byte*)dest) [0] = ((byte*)src) [0];
2987 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2989 ((short*)dest) [0] = ((short*)src) [0];
2990 ((short*)dest) [1] = ((short*)src) [1];
2991 ((short*)dest) [2] = ((short*)src) [2];
2992 ((short*)dest) [3] = ((short*)src) [3];
2998 ((short*)dest) [0] = ((short*)src) [0];
3004 ((byte*)dest) [0] = ((byte*)src) [0];
3006 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3008 ((byte*)dest) [0] = ((byte*)src) [0];
3009 ((byte*)dest) [1] = ((byte*)src) [1];
3010 ((byte*)dest) [2] = ((byte*)src) [2];
3011 ((byte*)dest) [3] = ((byte*)src) [3];
3012 ((byte*)dest) [4] = ((byte*)src) [4];
3013 ((byte*)dest) [5] = ((byte*)src) [5];
3014 ((byte*)dest) [6] = ((byte*)src) [6];
3015 ((byte*)dest) [7] = ((byte*)src) [7];
3021 ((byte*)dest) [0] = ((byte*)src) [0];
3022 ((byte*)dest) [1] = ((byte*)src) [1];
3028 ((byte*)dest) [0] = ((byte*)src) [0];
3031 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3032 // FIXME: if pointers are not aligned, try to align them
3033 // so a faster routine can be used. Handle the case where
3034 // the pointers can't be reduced to have the same alignment
3035 // (just ignore the issue on x86?)
3036 if ((((int)dest | (int)src) & 3) != 0) {
3037 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3043 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3044 ((short*)dest) [0] = ((short*)src) [0];
3049 if ((((int)dest | (int)src) & 1) != 0) {
3050 memcpy1 (dest, src, size);
3053 if ((((int)dest | (int)src) & 2) != 0) {
3054 memcpy2 (dest, src, size);
3058 memcpy4 (dest, src, size);
3061 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3062 // Same rules as for memcpy, but with the premise that
3063 // chars can only be aligned to even addresses if their
3064 // enclosing types are correctly aligned
3065 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3066 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3067 ((short*)dest) [0] = ((short*)src) [0];
3072 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3073 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3077 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3080 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3084 for (int i = count; i > 0; i--) {
3091 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3093 fixed (char* dest = target, src = source)
3094 CharCopy (dest + targetIndex, src + sourceIndex, count);
3097 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3099 fixed (char* dest = target, src = source)
3100 CharCopy (dest + targetIndex, src + sourceIndex, count);
3103 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3104 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3106 fixed (char* dest = target, src = source)
3107 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3110 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3111 unsafe public extern String (char *value);
3113 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3114 unsafe public extern String (char *value, int startIndex, int length);
3116 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3117 unsafe public extern String (sbyte *value);
3119 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3120 unsafe public extern String (sbyte *value, int startIndex, int length);
3122 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3123 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3125 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3126 public extern String (char [] value, int startIndex, int length);
3128 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3129 public extern String (char [] value);
3131 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3132 public extern String (char c, int count);
3134 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3135 internal extern static String InternalAllocateStr (int length);
3137 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3138 private extern static string InternalIntern (string str);
3140 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3141 private extern static string InternalIsInterned (string str);
3143 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3144 private extern static int GetLOSLimit ();