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 while (ptr < format.length) {
1957 char c = format[ptr ++];
1960 result.Append (format, start, ptr - start - 1);
1962 // check for escaped open bracket
1964 if (format[ptr] == '{') {
1975 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1976 if (n >= args.Length)
1977 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1981 object arg = args[n];
1984 ICustomFormatter formatter = null;
1985 if (provider != null)
1986 formatter = provider.GetFormat (typeof (ICustomFormatter))
1987 as ICustomFormatter;
1990 else if (formatter != null)
1991 str = formatter.Format (arg_format, arg, provider);
1992 else if (arg is IFormattable)
1993 str = ((IFormattable)arg).ToString (arg_format, provider);
1995 str = arg.ToString ();
1997 // pad formatted string and append to result
1999 if (width > str.length) {
2000 const char padchar = ' ';
2001 int padlen = width - str.length;
2004 result.Append (str);
2005 result.Append (padchar, padlen);
2008 result.Append (padchar, padlen);
2009 result.Append (str);
2013 result.Append (str);
2017 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2018 result.Append (format, start, ptr - start - 1);
2021 else if (c == '}') {
2022 throw new FormatException ("Input string was not in a correct format.");
2026 if (start < format.length)
2027 result.Append (format, start, format.Length - start);
2032 public unsafe static String Copy (String str)
2035 throw new ArgumentNullException ("str");
2037 int length = str.length;
2039 String tmp = InternalAllocateStr (length);
2041 fixed (char *dest = tmp, src = str) {
2042 CharCopy (dest, src, length);
2048 public static String Concat (Object arg0)
2053 return arg0.ToString ();
2056 public static String Concat (Object arg0, Object arg1)
2058 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2061 public static String Concat (Object arg0, Object arg1, Object arg2)
2067 s1 = arg0.ToString ();
2072 s2 = arg1.ToString ();
2077 s3 = arg2.ToString ();
2079 return Concat (s1, s2, s3);
2082 [CLSCompliant(false)]
2083 public static String Concat (Object arg0, Object arg1, Object arg2,
2084 Object arg3, __arglist)
2086 string s1, s2, s3, s4;
2091 s1 = arg0.ToString ();
2096 s2 = arg1.ToString ();
2101 s3 = arg2.ToString ();
2103 ArgIterator iter = new ArgIterator (__arglist);
2104 int argCount = iter.GetRemainingCount();
2106 StringBuilder sb = new StringBuilder ();
2108 sb.Append (arg3.ToString ());
2110 for (int i = 0; i < argCount; i++) {
2111 TypedReference typedRef = iter.GetNextArg ();
2112 sb.Append (TypedReference.ToObject (typedRef));
2115 s4 = sb.ToString ();
2117 return Concat (s1, s2, s3, s4);
2120 public unsafe static String Concat (String str0, String str1)
2122 if (str0 == null || str0.Length == 0) {
2123 if (str1 == null || str1.Length == 0)
2128 if (str1 == null || str1.Length == 0)
2131 String tmp = InternalAllocateStr (str0.length + str1.length);
2133 fixed (char *dest = tmp, src = str0)
2134 CharCopy (dest, src, str0.length);
2135 fixed (char *dest = tmp, src = str1)
2136 CharCopy (dest + str0.Length, src, str1.length);
2141 public unsafe static String Concat (String str0, String str1, String str2)
2143 if (str0 == null || str0.Length == 0){
2144 if (str1 == null || str1.Length == 0){
2145 if (str2 == null || str2.Length == 0)
2149 if (str2 == null || str2.Length == 0)
2154 if (str1 == null || str1.Length == 0){
2155 if (str2 == null || str2.Length == 0)
2160 if (str2 == null || str2.Length == 0)
2165 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2167 if (str0.Length != 0) {
2168 fixed (char *dest = tmp, src = str0) {
2169 CharCopy (dest, src, str0.length);
2172 if (str1.Length != 0) {
2173 fixed (char *dest = tmp, src = str1) {
2174 CharCopy (dest + str0.Length, src, str1.length);
2177 if (str2.Length != 0) {
2178 fixed (char *dest = tmp, src = str2) {
2179 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2186 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2188 if (str0 == null && str1 == null && str2 == null && str3 == null)
2200 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2202 if (str0.Length != 0) {
2203 fixed (char *dest = tmp, src = str0) {
2204 CharCopy (dest, src, str0.length);
2207 if (str1.Length != 0) {
2208 fixed (char *dest = tmp, src = str1) {
2209 CharCopy (dest + str0.Length, src, str1.length);
2212 if (str2.Length != 0) {
2213 fixed (char *dest = tmp, src = str2) {
2214 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2217 if (str3.Length != 0) {
2218 fixed (char *dest = tmp, src = str3) {
2219 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2226 public static String Concat (params Object[] args)
2229 throw new ArgumentNullException ("args");
2231 int argLen = args.Length;
2235 string [] strings = new string [argLen];
2237 for (int i = 0; i < argLen; i++) {
2238 if (args[i] != null) {
2239 strings[i] = args[i].ToString ();
2240 len += strings[i].length;
2244 return ConcatInternal (strings, len);
2247 public static String Concat (params String[] values)
2250 throw new ArgumentNullException ("values");
2253 for (int i = 0; i < values.Length; i++) {
2254 String s = values[i];
2259 return ConcatInternal (values, len);
2262 private static unsafe String ConcatInternal (String[] values, int length)
2267 String tmp = InternalAllocateStr (length);
2269 fixed (char* dest = tmp) {
2271 for (int i = 0; i < values.Length; i++) {
2272 String source = values[i];
2273 if (source != null) {
2274 fixed (char* src = source) {
2275 CharCopy (dest + pos, src, source.length);
2277 pos += source.Length;
2284 public unsafe String Insert (int startIndex, String value)
2287 throw new ArgumentNullException ("value");
2289 if (startIndex < 0 || startIndex > this.length)
2290 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2292 if (value.Length == 0)
2294 if (this.Length == 0)
2296 String tmp = InternalAllocateStr (this.length + value.length);
2298 fixed (char *dest = tmp, src = this, val = value) {
2300 CharCopy (dst, src, startIndex);
2302 CharCopy (dst, val, value.length);
2303 dst += value.length;
2304 CharCopy (dst, src + startIndex, length - startIndex);
2309 public static string Intern (string str)
2312 throw new ArgumentNullException ("str");
2314 return InternalIntern (str);
2317 public static string IsInterned (string str)
2320 throw new ArgumentNullException ("str");
2322 return InternalIsInterned (str);
2325 #if NET_4_0 || MOONLIGHT || MOBILE
2326 public static string Join (string separator, params string [] value)
2328 public static string Join (string separator, string [] value)
2332 throw new ArgumentNullException ("value");
2333 if (separator == null)
2336 return JoinUnchecked (separator, value, 0, value.Length);
2339 public static string Join (string separator, string[] value, int startIndex, int count)
2342 throw new ArgumentNullException ("value");
2344 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2346 throw new ArgumentOutOfRangeException ("count", "< 0");
2347 if (startIndex > value.Length - count)
2348 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2350 if (startIndex == value.Length)
2352 if (separator == null)
2355 return JoinUnchecked (separator, value, startIndex, count);
2358 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2360 // Unchecked parameters
2361 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2362 // separator and value must not be null
2365 int maxIndex = startIndex + count;
2366 // Precount the number of characters that the resulting string will have
2367 for (int i = startIndex; i < maxIndex; i++) {
2368 String s = value[i];
2372 length += separator.length * (count - 1);
2376 String tmp = InternalAllocateStr (length);
2379 fixed (char* dest = tmp, sepsrc = separator) {
2380 // Copy each string from value except the last one and add a separator for each
2382 for (int i = startIndex; i < maxIndex; i++) {
2383 String source = value[i];
2384 if (source != null) {
2385 if (source.Length > 0) {
2386 fixed (char* src = source)
2387 CharCopy (dest + pos, src, source.Length);
2388 pos += source.Length;
2391 if (separator.Length > 0) {
2392 CharCopy (dest + pos, sepsrc, separator.Length);
2393 pos += separator.Length;
2396 // Append last string that does not get an additional separator
2397 String sourceLast = value[maxIndex];
2398 if (sourceLast != null) {
2399 if (sourceLast.Length > 0) {
2400 fixed (char* src = sourceLast)
2401 CharCopy (dest + pos, src, sourceLast.Length);
2408 bool IConvertible.ToBoolean (IFormatProvider provider)
2410 return Convert.ToBoolean (this, provider);
2413 byte IConvertible.ToByte (IFormatProvider provider)
2415 return Convert.ToByte (this, provider);
2418 char IConvertible.ToChar (IFormatProvider provider)
2420 return Convert.ToChar (this, provider);
2423 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2425 return Convert.ToDateTime (this, provider);
2428 decimal IConvertible.ToDecimal (IFormatProvider provider)
2430 return Convert.ToDecimal (this, provider);
2433 double IConvertible.ToDouble (IFormatProvider provider)
2435 return Convert.ToDouble (this, provider);
2438 short IConvertible.ToInt16 (IFormatProvider provider)
2440 return Convert.ToInt16 (this, provider);
2443 int IConvertible.ToInt32 (IFormatProvider provider)
2445 return Convert.ToInt32 (this, provider);
2448 long IConvertible.ToInt64 (IFormatProvider provider)
2450 return Convert.ToInt64 (this, provider);
2453 sbyte IConvertible.ToSByte (IFormatProvider provider)
2455 return Convert.ToSByte (this, provider);
2458 float IConvertible.ToSingle (IFormatProvider provider)
2460 return Convert.ToSingle (this, provider);
2463 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2465 if (targetType == null)
2466 throw new ArgumentNullException ("type");
2467 return Convert.ToType (this, targetType, provider, false);
2470 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2472 return Convert.ToUInt16 (this, provider);
2475 uint IConvertible.ToUInt32 (IFormatProvider provider)
2477 return Convert.ToUInt32 (this, provider);
2480 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2482 return Convert.ToUInt64 (this, provider);
2491 public CharEnumerator GetEnumerator ()
2493 return new CharEnumerator (this);
2496 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2498 return new CharEnumerator (this);
2501 IEnumerator IEnumerable.GetEnumerator ()
2503 return new CharEnumerator (this);
2506 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2507 out bool left_align, out string format)
2509 int max = str.Length;
2511 // parses format specifier of form:
2515 // N = argument number (non-negative integer)
2517 n = ParseDecimal (str, ref ptr);
2519 throw new FormatException ("Input string was not in a correct format.");
2521 // M = width (non-negative integer)
2523 if (ptr < max && str[ptr] == ',') {
2524 // White space between ',' and number or sign.
2526 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2530 format = str.Substring (start, ptr - start);
2532 left_align = (ptr < max && str [ptr] == '-');
2536 width = ParseDecimal (str, ref ptr);
2538 throw new FormatException ("Input string was not in a correct format.");
2546 // F = argument format (string)
2548 if (ptr < max && str[ptr] == ':') {
2550 while (ptr < max && str[ptr] != '}')
2553 format += str.Substring (start, ptr - start);
2558 if ((ptr >= max) || str[ptr ++] != '}')
2559 throw new FormatException ("Input string was not in a correct format.");
2562 private static int ParseDecimal (string str, ref int ptr)
2566 int max = str.Length;
2570 if (c < '0' || '9' < c)
2573 n = n * 10 + c - '0';
2577 if (p == ptr || p == max)
2584 internal unsafe void InternalSetChar (int idx, char val)
2586 if ((uint) idx >= (uint) Length)
2587 throw new ArgumentOutOfRangeException ("idx");
2589 fixed (char * pStr = &start_char)
2595 internal unsafe void InternalSetLength (int newLength)
2597 if (newLength > length)
2598 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2600 // zero terminate, we can pass string objects directly via pinvoke
2601 // we also zero the rest of the string, since the new GC needs to be
2602 // able to handle the changing size (it will skip the 0 bytes).
2603 fixed (char * pStr = &start_char) {
2604 char *p = pStr + newLength;
2605 char *end = pStr + length;
2614 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2615 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2616 public unsafe override int GetHashCode ()
2618 fixed (char * c = this) {
2620 char * end = cc + length - 1;
2622 for (;cc < end; cc += 2) {
2623 h = (h << 5) - h + *cc;
2624 h = (h << 5) - h + cc [1];
2628 h = (h << 5) - h + *cc;
2633 #if MOONLIGHT || MOBILE || NET_4_0
2635 public static string Concat (IEnumerable<string> values)
2638 throw new ArgumentNullException ("values");
2640 var stringList = new List<string> ();
2642 foreach (var v in values){
2648 return ConcatInternal (stringList.ToArray (), len);
2651 [ComVisibleAttribute(false)]
2652 public static string Concat<T> (IEnumerable<T> values)
2655 throw new ArgumentNullException ("values");
2657 var stringList = new List<string> ();
2659 foreach (var v in values){
2660 string sr = v.ToString ();
2662 stringList.Add (sr);
2664 return ConcatInternal (stringList.ToArray (), len);
2667 [ComVisibleAttribute(false)]
2668 public static string Join (string separator, IEnumerable<string> values)
2670 if (separator == null)
2671 return Concat (values);
2674 throw new ArgumentNullException ("values");
2676 var stringList = new List<string> ();
2677 foreach (var v in values)
2680 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2683 [ComVisibleAttribute(false)]
2684 public static string Join (string separator, params object [] values)
2686 if (separator == null)
2687 return Concat (values);
2690 throw new ArgumentNullException ("values");
2692 var strCopy = new string [values.Length];
2694 foreach (var v in values)
2695 strCopy [i++] = v.ToString ();
2697 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2700 [ComVisible (false)]
2701 public static string Join<T> (string separator, IEnumerable<T> values)
2703 if (separator == null)
2704 return Concat<T> (values);
2707 throw new ArgumentNullException ("values");
2709 var stringList = new List<string> ();
2710 foreach (var v in values)
2711 stringList.Add (v.ToString ());
2713 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2716 public static bool IsNullOrWhiteSpace (string value)
2718 internal static bool IsNullOrWhiteSpace (string value)
2721 if ((value == null) || (value.Length == 0))
2723 foreach (char c in value)
2724 if (!Char.IsWhiteSpace (c))
2729 internal unsafe int GetCaseInsensitiveHashCode ()
2731 fixed (char * c = this) {
2733 char * end = cc + length - 1;
2735 for (;cc < end; cc += 2) {
2736 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2737 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2741 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2746 // Certain constructors are redirected to CreateString methods with
2747 // matching argument list. The this pointer should not be used.
2749 private unsafe String CreateString (sbyte* value)
2754 byte* bytes = (byte*) value;
2758 while (bytes++ [0] != 0)
2760 } catch (NullReferenceException) {
2761 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2762 } catch (AccessViolationException) {
2763 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2766 return CreateString (value, 0, length, null);
2769 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2771 return CreateString (value, startIndex, length, null);
2774 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2777 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2779 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2780 if (value + startIndex < value)
2781 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2783 bool isDefaultEncoding;
2785 if (isDefaultEncoding = (enc == null)) {
2787 throw new ArgumentNullException ("value");
2791 enc = Encoding.Default;
2794 byte [] bytes = new byte [length];
2797 fixed (byte* bytePtr = bytes)
2799 memcpy (bytePtr, (byte*) (value + startIndex), length);
2800 } catch (NullReferenceException) {
2801 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2802 } catch (AccessViolationException) {
2803 if (!isDefaultEncoding)
2806 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2809 // GetString () is called even when length == 0
2810 return enc.GetString (bytes);
2813 unsafe string CreateString (char *value)
2823 string result = InternalAllocateStr (i);
2826 fixed (char *dest = result) {
2827 CharCopy (dest, value, i);
2833 unsafe string CreateString (char *value, int startIndex, int length)
2838 throw new ArgumentNullException ("value");
2840 throw new ArgumentOutOfRangeException ("startIndex");
2842 throw new ArgumentOutOfRangeException ("length");
2844 string result = InternalAllocateStr (length);
2846 fixed (char *dest = result) {
2847 CharCopy (dest, value + startIndex, length);
2852 unsafe string CreateString (char [] val, int startIndex, int length)
2855 throw new ArgumentNullException ("value");
2857 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2859 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2860 if (startIndex > val.Length - length)
2861 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2865 string result = InternalAllocateStr (length);
2867 fixed (char *dest = result, src = val) {
2868 CharCopy (dest, src + startIndex, length);
2873 unsafe string CreateString (char [] val)
2875 if (val == null || val.Length == 0)
2877 string result = InternalAllocateStr (val.Length);
2879 fixed (char *dest = result, src = val) {
2880 CharCopy (dest, src, val.Length);
2885 unsafe string CreateString (char c, int count)
2888 throw new ArgumentOutOfRangeException ("count");
2891 string result = InternalAllocateStr (count);
2892 fixed (char *dest = result) {
2894 char *end = p + count;
2903 /* helpers used by the runtime as well as above or eslewhere in corlib */
2904 internal static unsafe void memset (byte *dest, int val, int len)
2915 val = val | (val << 8);
2916 val = val | (val << 16);
2919 int rest = (int)dest & 3;
2927 } while (rest != 0);
2930 ((int*)dest) [0] = val;
2931 ((int*)dest) [1] = val;
2932 ((int*)dest) [2] = val;
2933 ((int*)dest) [3] = val;
2938 ((int*)dest) [0] = val;
2950 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2951 /*while (size >= 32) {
2952 // using long is better than int and slower than double
2953 // FIXME: enable this only on correct alignment or on platforms
2954 // that can tolerate unaligned reads/writes of doubles
2955 ((double*)dest) [0] = ((double*)src) [0];
2956 ((double*)dest) [1] = ((double*)src) [1];
2957 ((double*)dest) [2] = ((double*)src) [2];
2958 ((double*)dest) [3] = ((double*)src) [3];
2963 while (size >= 16) {
2964 ((int*)dest) [0] = ((int*)src) [0];
2965 ((int*)dest) [1] = ((int*)src) [1];
2966 ((int*)dest) [2] = ((int*)src) [2];
2967 ((int*)dest) [3] = ((int*)src) [3];
2973 ((int*)dest) [0] = ((int*)src) [0];
2979 ((byte*)dest) [0] = ((byte*)src) [0];
2985 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2987 ((short*)dest) [0] = ((short*)src) [0];
2988 ((short*)dest) [1] = ((short*)src) [1];
2989 ((short*)dest) [2] = ((short*)src) [2];
2990 ((short*)dest) [3] = ((short*)src) [3];
2996 ((short*)dest) [0] = ((short*)src) [0];
3002 ((byte*)dest) [0] = ((byte*)src) [0];
3004 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3006 ((byte*)dest) [0] = ((byte*)src) [0];
3007 ((byte*)dest) [1] = ((byte*)src) [1];
3008 ((byte*)dest) [2] = ((byte*)src) [2];
3009 ((byte*)dest) [3] = ((byte*)src) [3];
3010 ((byte*)dest) [4] = ((byte*)src) [4];
3011 ((byte*)dest) [5] = ((byte*)src) [5];
3012 ((byte*)dest) [6] = ((byte*)src) [6];
3013 ((byte*)dest) [7] = ((byte*)src) [7];
3019 ((byte*)dest) [0] = ((byte*)src) [0];
3020 ((byte*)dest) [1] = ((byte*)src) [1];
3026 ((byte*)dest) [0] = ((byte*)src) [0];
3029 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3030 // FIXME: if pointers are not aligned, try to align them
3031 // so a faster routine can be used. Handle the case where
3032 // the pointers can't be reduced to have the same alignment
3033 // (just ignore the issue on x86?)
3034 if ((((int)dest | (int)src) & 3) != 0) {
3035 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3041 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3042 ((short*)dest) [0] = ((short*)src) [0];
3047 if ((((int)dest | (int)src) & 1) != 0) {
3048 memcpy1 (dest, src, size);
3051 if ((((int)dest | (int)src) & 2) != 0) {
3052 memcpy2 (dest, src, size);
3056 memcpy4 (dest, src, size);
3059 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3060 // Same rules as for memcpy, but with the premise that
3061 // chars can only be aligned to even addresses if their
3062 // enclosing types are correctly aligned
3063 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3064 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3065 ((short*)dest) [0] = ((short*)src) [0];
3070 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3071 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3075 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3078 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3082 for (int i = count; i > 0; i--) {
3089 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3091 fixed (char* dest = target, src = source)
3092 CharCopy (dest + targetIndex, src + sourceIndex, count);
3095 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3097 fixed (char* dest = target, src = source)
3098 CharCopy (dest + targetIndex, src + sourceIndex, count);
3101 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3102 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3104 fixed (char* dest = target, src = source)
3105 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3108 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3109 unsafe public extern String (char *value);
3111 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3112 unsafe public extern String (char *value, int startIndex, int length);
3114 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3115 unsafe public extern String (sbyte *value);
3117 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3118 unsafe public extern String (sbyte *value, int startIndex, int length);
3120 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3121 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3123 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3124 public extern String (char [] value, int startIndex, int length);
3126 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3127 public extern String (char [] value);
3129 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3130 public extern String (char c, int count);
3132 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3133 internal extern static String InternalAllocateStr (int length);
3135 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3136 private extern static string InternalIntern (string str);
3138 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3139 private extern static string InternalIsInterned (string str);
3141 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3142 private extern static int GetLOSLimit ();