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)
14 // Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 // This class contains all implementation for culture-insensitive methods.
38 // Culture-sensitive methods are implemented in the System.Globalization or
39 // Mono.Globalization namespace.
41 // Ensure that argument checks on methods don't overflow
45 using System.Collections;
46 using System.Globalization;
47 using System.Runtime.CompilerServices;
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
59 [StructLayout (LayoutKind.Sequential)]
60 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
62 [NonSerialized] private int length;
63 [NonSerialized] private char start_char;
65 public static readonly String Empty = "";
67 internal static readonly int LOS_limit = GetLOSLimit ();
69 public static unsafe bool Equals (string a, string b)
71 if ((a as object) == (b as object))
74 if (a == null || b == null)
82 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
87 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
88 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
89 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
90 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
99 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
100 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
109 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
117 return len == 0 || *s1_ptr == *s2_ptr;
121 public static bool operator == (String a, String b)
123 return Equals (a, b);
126 public static bool operator != (String a, String b)
128 return !Equals (a, b);
131 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
132 public override bool Equals (Object obj)
134 return Equals (this, obj as String);
137 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public bool Equals (String value)
140 return Equals (this, value);
143 [IndexerName ("Chars")]
144 public unsafe char this [int index] {
146 if (index < 0 || index >= length)
147 throw new IndexOutOfRangeException ();
148 fixed (char* c = &start_char)
153 public Object Clone ()
158 public TypeCode GetTypeCode ()
160 return TypeCode.String;
163 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
165 if (destination == null)
166 throw new ArgumentNullException ("destination");
168 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
169 if (destinationIndex < 0)
170 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
172 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
173 if (sourceIndex > Length - count)
174 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
175 if (destinationIndex > destination.Length - count)
176 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
178 fixed (char* dest = destination, src = this)
179 CharCopy (dest + destinationIndex, src + sourceIndex, count);
182 public char[] ToCharArray ()
184 return ToCharArray (0, length);
187 public unsafe char[] ToCharArray (int startIndex, int length)
190 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
192 throw new ArgumentOutOfRangeException ("length", "< 0");
193 if (startIndex > this.length - length)
194 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
196 char[] tmp = new char [length];
197 fixed (char* dest = tmp, src = this)
198 CharCopy (dest, src + startIndex, length);
202 public String [] Split (params char [] separator)
204 return Split (separator, int.MaxValue, 0);
207 public String[] Split (char[] separator, int count)
209 return Split (separator, count, 0);
213 public String[] Split (char[] separator, StringSplitOptions options)
215 return Split (separator, Int32.MaxValue, options);
219 public String[] Split (char[] separator, int count, StringSplitOptions options)
222 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
223 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
224 throw new ArgumentException ("Illegal enum value: " + options + ".");
226 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
227 return EmptyArray<string>.Value;
231 EmptyArray<string>.Value :
232 new String[1] { this };
235 return SplitByCharacters (separator, count, options != 0);
239 public String[] Split (string[] separator, StringSplitOptions options)
241 return Split (separator, Int32.MaxValue, options);
245 public String[] Split (string[] separator, int count, StringSplitOptions options)
248 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
249 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
250 throw new ArgumentException ("Illegal enum value: " + options + ".");
254 EmptyArray<string>.Value :
255 new String[1] { this };
258 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
260 if (separator == null || separator.Length == 0)
261 return SplitByCharacters (null, count, removeEmpty);
263 if (Length == 0 && removeEmpty)
264 return EmptyArray<string>.Value;
266 List<String> arr = new List<String> ();
270 while (pos < this.Length) {
272 int matchPos = Int32.MaxValue;
274 // Find the first position where any of the separators matches
275 for (int i = 0; i < separator.Length; ++i) {
276 string sep = separator [i];
277 if (sep == null || sep.Length == 0)
280 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
281 if (match >= 0 && match < matchPos) {
287 if (matchIndex == -1)
290 if (!(matchPos == pos && removeEmpty)) {
291 if (arr.Count == count - 1)
293 arr.Add (this.Substring (pos, matchPos - pos));
296 pos = matchPos + separator [matchIndex].Length;
302 return new String [] { this };
304 // string contained only separators
305 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
306 return EmptyArray<string>.Value;
308 if (!(removeEmpty && pos == this.Length))
309 arr.Add (this.Substring (pos));
311 return arr.ToArray ();
314 // .NET 2.0 compatibility only
315 #if !NET_4_0 && !MOBILE
316 static readonly char[] WhiteChars = {
317 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
318 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
319 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
320 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
321 (char) 0x3000, (char) 0xFEFF
325 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
327 #if !NET_4_0 && !MOBILE
328 if (sep == null || sep.Length == 0)
332 int[] split_points = null;
333 int total_points = 0;
336 if (sep == null || sep.Length == 0) {
337 fixed (char* src = this) {
342 if (char.IsWhiteSpace (*src_ptr++)) {
343 if (split_points == null) {
344 split_points = new int[8];
345 } else if (split_points.Length == total_points) {
346 Array.Resize (ref split_points, split_points.Length * 2);
349 split_points[total_points++] = Length - len;
350 if (total_points == count && !removeEmpty)
357 fixed (char* src = this) {
358 fixed (char* sep_src = sep) {
360 char* sep_ptr_end = sep_src + sep.Length;
363 char* sep_ptr = sep_src;
365 if (*sep_ptr++ == *src_ptr) {
366 if (split_points == null) {
367 split_points = new int[8];
368 } else if (split_points.Length == total_points) {
369 Array.Resize (ref split_points, split_points.Length * 2);
372 split_points[total_points++] = Length - len;
373 if (total_points == count && !removeEmpty)
378 } while (sep_ptr != sep_ptr_end);
387 if (total_points == 0)
388 return new string[] { this };
390 var res = new string[Math.Min (total_points, count) + 1];
394 for (; i < total_points; ++i) {
395 var start = split_points[i];
396 res[i] = SubstringUnchecked (prev_index, start - prev_index);
397 prev_index = start + 1;
400 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
404 for (; i < total_points; ++i) {
405 var start = split_points[i];
406 length = start - prev_index;
411 res[used++] = SubstringUnchecked (prev_index, length);
414 prev_index = start + 1;
417 length = Length - prev_index;
419 res[used++] = SubstringUnchecked (prev_index, length);
421 if (used != res.Length)
422 Array.Resize (ref res, used);
428 public String Substring (int startIndex)
432 if (startIndex < 0 || startIndex > this.length)
433 throw new ArgumentOutOfRangeException ("startIndex");
435 return SubstringUnchecked (startIndex, this.length - startIndex);
438 public String Substring (int startIndex, int length)
441 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
443 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
444 if (startIndex > this.length)
445 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
446 if (startIndex > this.length - length)
447 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
448 if (startIndex == 0 && length == this.length)
451 return SubstringUnchecked (startIndex, length);
454 // This method is used by StringBuilder.ToString() and is expected to
455 // always create a new string object (or return String.Empty).
456 internal unsafe String SubstringUnchecked (int startIndex, int length)
461 string tmp = InternalAllocateStr (length);
462 fixed (char* dest = tmp, src = this) {
463 CharCopy (dest, src + startIndex, length);
468 public String Trim ()
472 int start = FindNotWhiteSpace (0, length, 1);
477 int end = FindNotWhiteSpace (length - 1, start, -1);
479 int newLength = end - start + 1;
480 if (newLength == length)
483 return SubstringUnchecked (start, newLength);
486 public String Trim (params char[] trimChars)
488 if (trimChars == null || trimChars.Length == 0)
493 int start = FindNotInTable (0, length, 1, trimChars);
498 int end = FindNotInTable (length - 1, start, -1, trimChars);
500 int newLength = end - start + 1;
501 if (newLength == length)
504 return SubstringUnchecked (start, newLength);
507 public String TrimStart (params char[] trimChars)
512 if (trimChars == null || trimChars.Length == 0)
513 start = FindNotWhiteSpace (0, length, 1);
515 start = FindNotInTable (0, length, 1, trimChars);
520 return SubstringUnchecked (start, length - start);
523 public String TrimEnd (params char[] trimChars)
528 if (trimChars == null || trimChars.Length == 0)
529 end = FindNotWhiteSpace (length - 1, -1, -1);
531 end = FindNotInTable (length - 1, -1, -1, trimChars);
537 return SubstringUnchecked (0, end);
540 unsafe int FindNotWhiteSpace (int pos, int target, int change)
543 fixed (char* src = this) {
544 while (pos != target) {
545 if (!char.IsWhiteSpace (src[pos]))
552 while (pos != target) {
556 if (c < 0x9 || c > 0xD)
561 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
562 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
563 if (c < 0x2000 || c > 0x200B)
573 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
575 fixed (char* tablePtr = table, thisPtr = this) {
576 while (pos != target) {
577 char c = thisPtr[pos];
579 while (x < table.Length) {
580 if (c == tablePtr[x])
584 if (x == table.Length)
592 public static int Compare (String strA, String strB)
594 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
597 public static int Compare (String strA, String strB, bool ignoreCase)
599 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
602 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
605 throw new ArgumentNullException ("culture");
607 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
610 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
612 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
615 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
617 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
620 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
623 throw new ArgumentNullException ("culture");
625 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
626 throw new ArgumentOutOfRangeException ();
638 else if (strB == null) {
642 CompareOptions compopts;
645 compopts = CompareOptions.IgnoreCase;
647 compopts = CompareOptions.None;
649 // Need to cap the requested length to the
650 // length of the string, because
651 // CompareInfo.Compare will insist that length
652 // <= (string.Length - offset)
657 if (length > (strA.Length - indexA)) {
658 len1 = strA.Length - indexA;
661 if (length > (strB.Length - indexB)) {
662 len2 = strB.Length - indexB;
665 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
666 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
669 public static int Compare (string strA, string strB, StringComparison comparisonType)
671 switch (comparisonType) {
672 case StringComparison.CurrentCulture:
673 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
674 case StringComparison.CurrentCultureIgnoreCase:
675 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
676 case StringComparison.InvariantCulture:
677 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
678 case StringComparison.InvariantCultureIgnoreCase:
679 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
680 case StringComparison.Ordinal:
681 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
682 case StringComparison.OrdinalIgnoreCase:
683 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
685 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
686 throw new ArgumentException (msg, "comparisonType");
690 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
692 switch (comparisonType) {
693 case StringComparison.CurrentCulture:
694 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
695 case StringComparison.CurrentCultureIgnoreCase:
696 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
697 case StringComparison.InvariantCulture:
698 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
699 case StringComparison.InvariantCultureIgnoreCase:
700 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
701 case StringComparison.Ordinal:
702 return CompareOrdinal (strA, indexA, strB, indexB, length);
703 case StringComparison.OrdinalIgnoreCase:
704 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
706 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
707 throw new ArgumentException (msg, "comparisonType");
711 public static bool Equals (string a, string b, StringComparison comparisonType)
713 return Compare (a, b, comparisonType) == 0;
716 public bool Equals (string value, StringComparison comparisonType)
718 return Compare (value, this, comparisonType) == 0;
721 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
724 throw new ArgumentNullException ("culture");
726 return culture.CompareInfo.Compare (strA, strB, options);
729 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
732 throw new ArgumentNullException ("culture");
737 if (length > (strA.Length - indexA))
738 len1 = strA.Length - indexA;
740 if (length > (strB.Length - indexB))
741 len2 = strB.Length - indexB;
743 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
746 public int CompareTo (Object value)
751 if (!(value is String))
752 throw new ArgumentException ();
754 return String.Compare (this, (String) value);
757 public int CompareTo (String strB)
762 return Compare (this, strB);
765 public static int CompareOrdinal (String strA, String strB)
767 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
770 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
772 if (strA != null && strB != null)
774 if (indexA > strA.Length || indexA < 0)
775 throw new ArgumentOutOfRangeException ("indexA");
776 if (indexB > strB.Length || indexB < 0)
777 throw new ArgumentOutOfRangeException ("indexB");
779 throw new ArgumentOutOfRangeException ("length");
782 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
785 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
787 if (strA != null && strB != null)
789 if (indexA > strA.Length || indexA < 0)
790 throw new ArgumentOutOfRangeException ("indexA");
791 if (indexB > strB.Length || indexB < 0)
792 throw new ArgumentOutOfRangeException ("indexB");
794 throw new ArgumentOutOfRangeException ("length");
797 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
800 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
803 return strB == null ? 0 : -1;
808 int lengthA = Math.Min (lenA, strA.Length - indexA);
809 int lengthB = Math.Min (lenB, strB.Length - indexB);
811 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
814 fixed (char* aptr = strA, bptr = strB) {
815 char* ap = aptr + indexA;
816 char* end = ap + Math.Min (lengthA, lengthB);
817 char* bp = bptr + indexB;
824 return lengthA - lengthB;
829 // Fastest method for internal case insensitive comparison
831 internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
833 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
836 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
838 // Same as above, but checks versus uppercase characters
840 return strB == null ? 0 : -1;
845 int lengthA = Math.Min (lenA, strA.Length - indexA);
846 int lengthB = Math.Min (lenB, strB.Length - indexB);
848 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
851 fixed (char* aptr = strA, bptr = strB) {
852 char* ap = aptr + indexA;
853 char* end = ap + Math.Min (lengthA, lengthB);
854 char* bp = bptr + indexB;
857 char c1 = Char.ToUpperInvariant (*ap);
858 char c2 = Char.ToUpperInvariant (*bp);
865 return lengthA - lengthB;
869 public bool EndsWith (String value)
872 throw new ArgumentNullException ("value");
874 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
877 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
880 throw new ArgumentNullException ("value");
882 culture = CultureInfo.CurrentCulture;
884 return culture.CompareInfo.IsSuffix (this, value,
885 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
888 // Following methods are culture-insensitive
889 public int IndexOfAny (char [] anyOf)
892 throw new ArgumentNullException ();
893 if (this.length == 0)
896 return IndexOfAnyUnchecked (anyOf, 0, this.length);
899 public int IndexOfAny (char [] anyOf, int startIndex)
902 throw new ArgumentNullException ();
903 if (startIndex < 0 || startIndex > this.length)
904 throw new ArgumentOutOfRangeException ();
906 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
909 public int IndexOfAny (char [] anyOf, int startIndex, int count)
912 throw new ArgumentNullException ();
913 if (startIndex < 0 || startIndex > this.length)
914 throw new ArgumentOutOfRangeException ();
915 if (count < 0 || startIndex > this.length - count)
916 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
918 return IndexOfAnyUnchecked (anyOf, startIndex, count);
921 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
923 if (anyOf.Length == 0)
926 if (anyOf.Length == 1)
927 return IndexOfUnchecked (anyOf[0], startIndex, count);
929 fixed (char* any = anyOf) {
933 char* end_any_ptr = any + anyOf.Length;
935 while (++any_ptr != end_any_ptr) {
936 if (*any_ptr > highest) {
941 if (*any_ptr < lowest)
945 fixed (char* start = &start_char) {
946 char* ptr = start + startIndex;
947 char* end_ptr = ptr + count;
949 while (ptr != end_ptr) {
950 if (*ptr > highest || *ptr < lowest) {
956 return (int)(ptr - start);
959 while (++any_ptr != end_any_ptr) {
960 if (*ptr == *any_ptr)
961 return (int)(ptr - start);
972 public int IndexOf (string value, StringComparison comparisonType)
974 return IndexOf (value, 0, this.Length, comparisonType);
977 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
979 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
982 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
984 switch (comparisonType) {
985 case StringComparison.CurrentCulture:
986 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
987 case StringComparison.CurrentCultureIgnoreCase:
988 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
989 case StringComparison.InvariantCulture:
990 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
991 case StringComparison.InvariantCultureIgnoreCase:
992 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
993 case StringComparison.Ordinal:
994 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
995 case StringComparison.OrdinalIgnoreCase:
996 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
998 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
999 throw new ArgumentException (msg, "comparisonType");
1003 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1006 throw new ArgumentNullException ("value");
1008 throw new ArgumentOutOfRangeException ("startIndex");
1009 if (count < 0 || (this.length - startIndex) < count)
1010 throw new ArgumentOutOfRangeException ("count");
1012 if (options == CompareOptions.Ordinal)
1013 return IndexOfOrdinalUnchecked (value, startIndex, count);
1014 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1017 internal unsafe int IndexOfOrdinalUnchecked (string value)
1019 return IndexOfOrdinalUnchecked (value, 0, length);
1022 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
1024 int valueLen = value.Length;
1025 if (count < valueLen)
1028 if (valueLen <= 1) {
1030 return IndexOfUnchecked (value[0], startIndex, count);
1034 fixed (char* thisptr = this, valueptr = value) {
1035 char* ap = thisptr + startIndex;
1036 char* thisEnd = ap + count - valueLen + 1;
1037 while (ap != thisEnd) {
1038 if (*ap == *valueptr) {
1039 for (int i = 1; i < valueLen; i++) {
1040 if (ap[i] != valueptr[i])
1043 return (int)(ap - thisptr);
1052 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1054 int valueLen = value.Length;
1055 if (count < valueLen)
1061 fixed (char* thisptr = this, valueptr = value) {
1062 char* ap = thisptr + startIndex;
1063 char* thisEnd = ap + count - valueLen + 1;
1064 while (ap != thisEnd) {
1065 for (int i = 0; i < valueLen; i++) {
1066 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1069 return (int)(ap - thisptr);
1077 public int LastIndexOf (string value, StringComparison comparisonType)
1079 if (this.Length == 0)
1080 return value.Length == 0 ? 0 : -1;
1082 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1085 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1087 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1090 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1092 switch (comparisonType) {
1093 case StringComparison.CurrentCulture:
1094 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1095 case StringComparison.CurrentCultureIgnoreCase:
1096 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1097 case StringComparison.InvariantCulture:
1098 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1099 case StringComparison.InvariantCultureIgnoreCase:
1100 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1101 case StringComparison.Ordinal:
1102 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1103 case StringComparison.OrdinalIgnoreCase:
1104 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1106 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1107 throw new ArgumentException (msg, "comparisonType");
1111 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1114 throw new ArgumentNullException ("value");
1115 if (this.Length == 0)
1116 return value.Length == 0 ? 0 : -1;
1117 if (value.Length == 0)
1118 return Math.Min (this.Length - 1, startIndex);
1119 if (startIndex < 0 || startIndex > length)
1120 throw new ArgumentOutOfRangeException ("startIndex");
1121 if (count < 0 || (startIndex < count - 1))
1122 throw new ArgumentOutOfRangeException ("count");
1124 if (options == CompareOptions.Ordinal)
1125 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1126 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1129 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1131 int valueLen = value.Length;
1132 if (count < valueLen)
1135 if (valueLen <= 1) {
1137 return LastIndexOfUnchecked (value[0], startIndex, count);
1141 fixed (char* thisptr = this, valueptr = value) {
1142 char* ap = thisptr + startIndex - valueLen + 1;
1143 char* thisEnd = ap - count + valueLen - 1;
1144 while (ap != thisEnd) {
1145 if (*ap == *valueptr) {
1146 for (int i = 1; i < valueLen; i++) {
1147 if (ap[i] != valueptr[i])
1150 return (int)(ap - thisptr);
1159 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1161 int valueLen = value.Length;
1162 if (count < valueLen)
1168 fixed (char* thisptr = this, valueptr = value) {
1169 char* ap = thisptr + startIndex - valueLen + 1;
1170 char* thisEnd = ap - count + valueLen - 1;
1171 while (ap != thisEnd) {
1172 for (int i = 0; i < valueLen; i++) {
1173 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1176 return (int)(ap - thisptr);
1184 // Following methods are culture-insensitive
1185 public int IndexOf (char value)
1187 if (this.length == 0)
1190 return IndexOfUnchecked (value, 0, this.length);
1193 public int IndexOf (char value, int startIndex)
1196 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1197 if (startIndex > this.length)
1198 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1200 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1203 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1206 public int IndexOf (char value, int startIndex, int count)
1208 if (startIndex < 0 || startIndex > this.length)
1209 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1211 throw new ArgumentOutOfRangeException ("count", "< 0");
1212 if (startIndex > this.length - count)
1213 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1215 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1218 return IndexOfUnchecked (value, startIndex, count);
1221 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1223 // It helps JIT compiler to optimize comparison
1224 int value_32 = (int)value;
1226 fixed (char* start = &start_char) {
1227 char* ptr = start + startIndex;
1228 char* end_ptr = ptr + (count >> 3 << 3);
1230 while (ptr != end_ptr) {
1231 if (*ptr == value_32)
1232 return (int)(ptr - start);
1233 if (ptr[1] == value_32)
1234 return (int)(ptr - start + 1);
1235 if (ptr[2] == value_32)
1236 return (int)(ptr - start + 2);
1237 if (ptr[3] == value_32)
1238 return (int)(ptr - start + 3);
1239 if (ptr[4] == value_32)
1240 return (int)(ptr - start + 4);
1241 if (ptr[5] == value_32)
1242 return (int)(ptr - start + 5);
1243 if (ptr[6] == value_32)
1244 return (int)(ptr - start + 6);
1245 if (ptr[7] == value_32)
1246 return (int)(ptr - start + 7);
1251 end_ptr += count & 0x07;
1252 while (ptr != end_ptr) {
1253 if (*ptr == value_32)
1254 return (int)(ptr - start);
1262 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1266 int end = startIndex + count;
1267 char c = Char.ToUpperInvariant (value);
1268 fixed (char* s = &start_char) {
1269 for (int i = startIndex; i < end; i++)
1270 if (Char.ToUpperInvariant (s [i]) == c)
1276 // Following methods are culture-sensitive
1277 public int IndexOf (String value)
1280 throw new ArgumentNullException ("value");
1281 if (value.length == 0)
1283 if (this.length == 0)
1285 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.Ordinal);
1288 public int IndexOf (String value, int startIndex)
1290 return IndexOf (value, startIndex, this.length - startIndex);
1293 public int IndexOf (String value, int startIndex, int count)
1296 throw new ArgumentNullException ("value");
1297 if (startIndex < 0 || startIndex > this.length)
1298 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1299 if (count < 0 || startIndex > this.length - count)
1300 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1302 if (value.length == 0)
1305 if (startIndex == 0 && this.length == 0)
1311 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1314 // Following methods are culture-insensitive
1315 public int LastIndexOfAny (char [] anyOf)
1318 throw new ArgumentNullException ();
1319 if (this.length == 0)
1322 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1325 public int LastIndexOfAny (char [] anyOf, int startIndex)
1328 throw new ArgumentNullException ();
1329 if (this.length == 0)
1332 if (startIndex < 0 || startIndex >= this.length)
1333 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1335 if (this.length == 0)
1338 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1341 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1344 throw new ArgumentNullException ();
1345 if (this.length == 0)
1348 if ((startIndex < 0) || (startIndex >= this.Length))
1349 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1350 if ((count < 0) || (count > this.Length))
1351 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1352 if (startIndex - count + 1 < 0)
1353 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1355 if (this.length == 0)
1358 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1361 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1363 if (anyOf.Length == 1)
1364 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1366 fixed (char* start = this, testStart = anyOf) {
1367 char* ptr = start + startIndex;
1368 char* ptrEnd = ptr - count;
1370 char* testEnd = testStart + anyOf.Length;
1372 while (ptr != ptrEnd) {
1374 while (test != testEnd) {
1376 return (int)(ptr - start);
1385 // Following methods are culture-insensitive
1386 public int LastIndexOf (char value)
1388 if (this.length == 0)
1391 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1394 public int LastIndexOf (char value, int startIndex)
1396 return LastIndexOf (value, startIndex, startIndex + 1);
1399 public int LastIndexOf (char value, int startIndex, int count)
1401 if (this.length == 0)
1404 // >= for char (> for string)
1405 if ((startIndex < 0) || (startIndex >= this.Length))
1406 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1407 if ((count < 0) || (count > this.Length))
1408 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1409 if (startIndex - count + 1 < 0)
1410 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1412 return LastIndexOfUnchecked (value, startIndex, count);
1415 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1417 // It helps JIT compiler to optimize comparison
1418 int value_32 = (int)value;
1420 fixed (char* start = &start_char) {
1421 char* ptr = start + startIndex;
1422 char* end_ptr = ptr - (count >> 3 << 3);
1424 while (ptr != end_ptr) {
1425 if (*ptr == value_32)
1426 return (int)(ptr - start);
1427 if (ptr[-1] == value_32)
1428 return (int)(ptr - start) - 1;
1429 if (ptr[-2] == value_32)
1430 return (int)(ptr - start) - 2;
1431 if (ptr[-3] == value_32)
1432 return (int)(ptr - start) - 3;
1433 if (ptr[-4] == value_32)
1434 return (int)(ptr - start) - 4;
1435 if (ptr[-5] == value_32)
1436 return (int)(ptr - start) - 5;
1437 if (ptr[-6] == value_32)
1438 return (int)(ptr - start) - 6;
1439 if (ptr[-7] == value_32)
1440 return (int)(ptr - start) - 7;
1445 end_ptr -= count & 0x07;
1446 while (ptr != end_ptr) {
1447 if (*ptr == value_32)
1448 return (int)(ptr - start);
1456 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1460 int end = startIndex - count;
1461 char c = Char.ToUpperInvariant (value);
1462 fixed (char* s = &start_char) {
1463 for (int i = startIndex; i > end; i--)
1464 if (Char.ToUpperInvariant (s [i]) == c)
1470 // Following methods are culture-sensitive
1471 public int LastIndexOf (String value)
1473 return LastIndexOf (value, this.length - 1, this.length);
1476 public int LastIndexOf (String value, int startIndex)
1478 int max = startIndex;
1479 if (max < this.Length)
1481 return LastIndexOf (value, startIndex, max);
1484 public int LastIndexOf (String value, int startIndex, int count)
1487 throw new ArgumentNullException ("value");
1489 if (this.length == 0)
1490 return value.Length == 0 ? 0 : -1;
1491 // -1 > startIndex > for string (0 > startIndex >= for char)
1492 if ((startIndex < -1) || (startIndex > this.Length))
1493 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1494 if ((count < 0) || (count > this.Length))
1495 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1496 if (startIndex - count + 1 < 0)
1497 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1499 if (value.Length == 0)
1500 return Math.Min (this.Length - 1, startIndex);
1502 if (startIndex == 0 && this.length == 0)
1505 // This check is needed to match undocumented MS behaviour
1506 if (this.length == 0 && value.length > 0)
1512 if (startIndex == this.Length)
1514 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1517 public bool Contains (String value)
1519 return IndexOf (value) != -1;
1522 public static bool IsNullOrEmpty (String value)
1524 return (value == null) || (value.Length == 0);
1527 public string Normalize ()
1529 return Normalization.Normalize (this, 0);
1532 public string Normalize (NormalizationForm normalizationForm)
1534 switch (normalizationForm) {
1536 return Normalization.Normalize (this, 0);
1537 case NormalizationForm.FormD:
1538 return Normalization.Normalize (this, 1);
1539 case NormalizationForm.FormKC:
1540 return Normalization.Normalize (this, 2);
1541 case NormalizationForm.FormKD:
1542 return Normalization.Normalize (this, 3);
1546 public bool IsNormalized ()
1548 return Normalization.IsNormalized (this, 0);
1551 public bool IsNormalized (NormalizationForm normalizationForm)
1553 switch (normalizationForm) {
1555 return Normalization.IsNormalized (this, 0);
1556 case NormalizationForm.FormD:
1557 return Normalization.IsNormalized (this, 1);
1558 case NormalizationForm.FormKC:
1559 return Normalization.IsNormalized (this, 2);
1560 case NormalizationForm.FormKD:
1561 return Normalization.IsNormalized (this, 3);
1565 public string Remove (int startIndex)
1568 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1569 if (startIndex >= this.length)
1570 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1572 return Remove (startIndex, this.length - startIndex);
1575 public String PadLeft (int totalWidth)
1577 return PadLeft (totalWidth, ' ');
1580 public unsafe String PadLeft (int totalWidth, char paddingChar)
1582 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1585 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1587 if (totalWidth < this.length)
1589 if (totalWidth == 0)
1592 String tmp = InternalAllocateStr (totalWidth);
1594 fixed (char* dest = tmp, src = this) {
1595 char* padPos = dest;
1596 char* padTo = dest + (totalWidth - length);
1597 while (padPos != padTo)
1598 *padPos++ = paddingChar;
1600 CharCopy (padTo, src, length);
1605 public String PadRight (int totalWidth)
1607 return PadRight (totalWidth, ' ');
1610 public unsafe String PadRight (int totalWidth, char paddingChar)
1612 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1615 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1617 if (totalWidth < this.length)
1619 if (totalWidth == 0)
1622 String tmp = InternalAllocateStr (totalWidth);
1624 fixed (char* dest = tmp, src = this) {
1625 CharCopy (dest, src, length);
1627 char* padPos = dest + length;
1628 char* padTo = dest + totalWidth;
1629 while (padPos != padTo)
1630 *padPos++ = paddingChar;
1635 public bool StartsWith (String value)
1638 throw new ArgumentNullException ("value");
1640 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1643 [ComVisible (false)]
1644 public bool StartsWith (string value, StringComparison comparisonType)
1647 throw new ArgumentNullException ("value");
1649 switch (comparisonType) {
1650 case StringComparison.CurrentCulture:
1651 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1652 case StringComparison.CurrentCultureIgnoreCase:
1653 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1654 case StringComparison.InvariantCulture:
1655 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1656 case StringComparison.InvariantCultureIgnoreCase:
1657 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1658 case StringComparison.Ordinal:
1659 return StartsWithOrdinalUnchecked (value);
1660 case StringComparison.OrdinalIgnoreCase:
1661 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1663 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1664 throw new ArgumentException (msg, "comparisonType");
1668 internal bool StartsWithOrdinalUnchecked (string value)
1670 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1673 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1675 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1678 [ComVisible (false)]
1679 public bool EndsWith (string value, StringComparison comparisonType)
1682 throw new ArgumentNullException ("value");
1684 switch (comparisonType) {
1685 case StringComparison.CurrentCulture:
1686 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1687 case StringComparison.CurrentCultureIgnoreCase:
1688 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1689 case StringComparison.InvariantCulture:
1690 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1691 case StringComparison.InvariantCultureIgnoreCase:
1692 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1693 case StringComparison.Ordinal:
1694 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1695 case StringComparison.OrdinalIgnoreCase:
1696 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1698 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1699 throw new ArgumentException (msg, "comparisonType");
1703 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1705 if (culture == null)
1706 culture = CultureInfo.CurrentCulture;
1708 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1711 // Following method is culture-insensitive
1712 public unsafe String Replace (char oldChar, char newChar)
1714 if (this.length == 0 || oldChar == newChar)
1717 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1718 if (start_pos == -1)
1724 string tmp = InternalAllocateStr (length);
1725 fixed (char* dest = tmp, src = &start_char) {
1727 CharCopy (dest, src, start_pos);
1729 char* end_ptr = dest + length;
1730 char* dest_ptr = dest + start_pos;
1731 char* src_ptr = src + start_pos;
1733 while (dest_ptr != end_ptr) {
1734 if (*src_ptr == oldChar)
1735 *dest_ptr = newChar;
1737 *dest_ptr = *src_ptr;
1746 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1747 public String Replace (String oldValue, String newValue)
1749 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1750 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1752 if (oldValue == null)
1753 throw new ArgumentNullException ("oldValue");
1755 if (oldValue.Length == 0)
1756 throw new ArgumentException ("oldValue is the empty string.");
1758 if (this.Length == 0)
1761 if (newValue == null)
1764 return ReplaceUnchecked (oldValue, newValue);
1767 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1769 if (oldValue.length > length)
1771 if (oldValue.length == 1 && newValue.length == 1) {
1772 return Replace (oldValue[0], newValue[0]);
1773 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1774 // because the length of the result would be this.length and length calculation unneccesary
1777 const int maxValue = 200; // Allocate 800 byte maximum
1778 int* dat = stackalloc int[maxValue];
1779 fixed (char* source = this, replace = newValue) {
1780 int i = 0, count = 0;
1781 while (i < length) {
1782 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1786 if (count < maxValue)
1787 dat[count++] = found;
1789 return ReplaceFallback (oldValue, newValue, maxValue);
1791 i = found + oldValue.length;
1795 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1796 String tmp = InternalAllocateStr (nlen);
1798 int curPos = 0, lastReadPos = 0;
1799 fixed (char* dest = tmp) {
1800 for (int j = 0; j < count; j++) {
1801 int precopy = dat[j] - lastReadPos;
1802 CharCopy (dest + curPos, source + lastReadPos, precopy);
1804 lastReadPos = dat[j] + oldValue.length;
1805 CharCopy (dest + curPos, replace, newValue.length);
1806 curPos += newValue.length;
1808 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1814 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1816 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1817 StringBuilder sb = new StringBuilder (lengthEstimate);
1818 for (int i = 0; i < length;) {
1819 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1821 sb.Append (SubstringUnchecked (i, length - i));
1824 sb.Append (SubstringUnchecked (i, found - i));
1825 sb.Append (newValue);
1826 i = found + oldValue.Length;
1828 return sb.ToString ();
1832 public unsafe String Remove (int startIndex, int count)
1835 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1837 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1838 if (startIndex > this.length - count)
1839 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1841 String tmp = InternalAllocateStr (this.length - count);
1843 fixed (char *dest = tmp, src = this) {
1845 CharCopy (dst, src, startIndex);
1846 int skip = startIndex + count;
1848 CharCopy (dst, src + skip, length - skip);
1853 public String ToLower ()
1855 return ToLower (CultureInfo.CurrentCulture);
1858 public String ToLower (CultureInfo culture)
1860 if (culture == null)
1861 throw new ArgumentNullException ("culture");
1863 if (culture.LCID == 0x007F) // Invariant
1864 return ToLowerInvariant ();
1866 return culture.TextInfo.ToLower (this);
1869 public unsafe String ToLowerInvariant ()
1874 string tmp = InternalAllocateStr (length);
1875 fixed (char* source = &start_char, dest = tmp) {
1877 char* destPtr = (char*)dest;
1878 char* sourcePtr = (char*)source;
1880 for (int n = 0; n < length; n++) {
1881 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1889 public String ToUpper ()
1891 return ToUpper (CultureInfo.CurrentCulture);
1894 public String ToUpper (CultureInfo culture)
1896 if (culture == null)
1897 throw new ArgumentNullException ("culture");
1899 if (culture.LCID == 0x007F) // Invariant
1900 return ToUpperInvariant ();
1902 return culture.TextInfo.ToUpper (this);
1905 public unsafe String ToUpperInvariant ()
1910 string tmp = InternalAllocateStr (length);
1911 fixed (char* source = &start_char, dest = tmp) {
1913 char* destPtr = (char*)dest;
1914 char* sourcePtr = (char*)source;
1916 for (int n = 0; n < length; n++) {
1917 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1925 public override String ToString ()
1930 public String ToString (IFormatProvider provider)
1935 public static String Format (String format, Object arg0)
1937 return Format (null, format, new Object[] {arg0});
1940 public static String Format (String format, Object arg0, Object arg1)
1942 return Format (null, format, new Object[] {arg0, arg1});
1945 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1947 return Format (null, format, new Object[] {arg0, arg1, arg2});
1950 public static string Format (string format, params object[] args)
1952 return Format (null, format, args);
1955 public static string Format (IFormatProvider provider, string format, params object[] args)
1957 StringBuilder b = FormatHelper (null, provider, format, args);
1958 return b.ToString ();
1961 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1964 throw new ArgumentNullException ("format");
1966 throw new ArgumentNullException ("args");
1968 if (result == null) {
1969 /* Try to approximate the size of result to avoid reallocations */
1973 for (i = 0; i < args.Length; ++i) {
1974 string s = args [i] as string;
1980 if (i == args.Length)
1981 result = new StringBuilder (len + format.length);
1983 result = new StringBuilder ();
1988 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1990 while (ptr < format.length) {
1991 char c = format[ptr ++];
1994 result.Append (format, start, ptr - start - 1);
1996 // check for escaped open bracket
1998 if (format[ptr] == '{') {
2009 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2010 if (n >= args.Length)
2011 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2015 object arg = args[n];
2020 else if (formatter != null)
2021 str = formatter.Format (arg_format, arg, provider);
2026 if (arg is IFormattable)
2027 str = ((IFormattable)arg).ToString (arg_format, provider);
2029 str = arg.ToString ();
2032 // pad formatted string and append to result
2033 if (width > str.length) {
2034 const char padchar = ' ';
2035 int padlen = width - str.length;
2038 result.Append (str);
2039 result.Append (padchar, padlen);
2042 result.Append (padchar, padlen);
2043 result.Append (str);
2046 result.Append (str);
2051 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2052 result.Append (format, start, ptr - start - 1);
2055 else if (c == '}') {
2056 throw new FormatException ("Input string was not in a correct format.");
2060 if (start < format.length)
2061 result.Append (format, start, format.Length - start);
2066 public unsafe static String Copy (String str)
2069 throw new ArgumentNullException ("str");
2071 int length = str.length;
2073 String tmp = InternalAllocateStr (length);
2075 fixed (char *dest = tmp, src = str) {
2076 CharCopy (dest, src, length);
2082 public static String Concat (Object arg0)
2087 return arg0.ToString ();
2090 public static String Concat (Object arg0, Object arg1)
2092 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2095 public static String Concat (Object arg0, Object arg1, Object arg2)
2101 s1 = arg0.ToString ();
2106 s2 = arg1.ToString ();
2111 s3 = arg2.ToString ();
2113 return Concat (s1, s2, s3);
2116 [CLSCompliant(false)]
2117 public static String Concat (Object arg0, Object arg1, Object arg2,
2118 Object arg3, __arglist)
2120 string s1, s2, s3, s4;
2125 s1 = arg0.ToString ();
2130 s2 = arg1.ToString ();
2135 s3 = arg2.ToString ();
2137 ArgIterator iter = new ArgIterator (__arglist);
2138 int argCount = iter.GetRemainingCount();
2140 StringBuilder sb = new StringBuilder ();
2142 sb.Append (arg3.ToString ());
2144 for (int i = 0; i < argCount; i++) {
2145 TypedReference typedRef = iter.GetNextArg ();
2146 sb.Append (TypedReference.ToObject (typedRef));
2149 s4 = sb.ToString ();
2151 return Concat (s1, s2, s3, s4);
2154 public unsafe static String Concat (String str0, String str1)
2156 if (str0 == null || str0.Length == 0) {
2157 if (str1 == null || str1.Length == 0)
2162 if (str1 == null || str1.Length == 0)
2165 String tmp = InternalAllocateStr (str0.length + str1.length);
2167 fixed (char *dest = tmp, src = str0)
2168 CharCopy (dest, src, str0.length);
2169 fixed (char *dest = tmp, src = str1)
2170 CharCopy (dest + str0.Length, src, str1.length);
2175 public unsafe static String Concat (String str0, String str1, String str2)
2177 if (str0 == null || str0.Length == 0){
2178 if (str1 == null || str1.Length == 0){
2179 if (str2 == null || str2.Length == 0)
2183 if (str2 == null || str2.Length == 0)
2188 if (str1 == null || str1.Length == 0){
2189 if (str2 == null || str2.Length == 0)
2194 if (str2 == null || str2.Length == 0)
2199 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2201 if (str0.Length != 0) {
2202 fixed (char *dest = tmp, src = str0) {
2203 CharCopy (dest, src, str0.length);
2206 if (str1.Length != 0) {
2207 fixed (char *dest = tmp, src = str1) {
2208 CharCopy (dest + str0.Length, src, str1.length);
2211 if (str2.Length != 0) {
2212 fixed (char *dest = tmp, src = str2) {
2213 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2220 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2222 if (str0 == null && str1 == null && str2 == null && str3 == null)
2234 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2236 if (str0.Length != 0) {
2237 fixed (char *dest = tmp, src = str0) {
2238 CharCopy (dest, src, str0.length);
2241 if (str1.Length != 0) {
2242 fixed (char *dest = tmp, src = str1) {
2243 CharCopy (dest + str0.Length, src, str1.length);
2246 if (str2.Length != 0) {
2247 fixed (char *dest = tmp, src = str2) {
2248 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2251 if (str3.Length != 0) {
2252 fixed (char *dest = tmp, src = str3) {
2253 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2260 public static String Concat (params Object[] args)
2263 throw new ArgumentNullException ("args");
2265 int argLen = args.Length;
2269 string [] strings = new string [argLen];
2271 for (int i = 0; i < argLen; i++) {
2272 if (args[i] != null) {
2273 strings[i] = args[i].ToString ();
2274 len += strings[i].length;
2278 return ConcatInternal (strings, len);
2281 public static String Concat (params String[] values)
2284 throw new ArgumentNullException ("values");
2287 for (int i = 0; i < values.Length; i++) {
2288 String s = values[i];
2293 return ConcatInternal (values, len);
2296 private static unsafe String ConcatInternal (String[] values, int length)
2301 String tmp = InternalAllocateStr (length);
2303 fixed (char* dest = tmp) {
2305 for (int i = 0; i < values.Length; i++) {
2306 String source = values[i];
2307 if (source != null) {
2308 fixed (char* src = source) {
2309 CharCopy (dest + pos, src, source.length);
2311 pos += source.Length;
2318 public unsafe String Insert (int startIndex, String value)
2321 throw new ArgumentNullException ("value");
2323 if (startIndex < 0 || startIndex > this.length)
2324 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2326 if (value.Length == 0)
2328 if (this.Length == 0)
2330 String tmp = InternalAllocateStr (this.length + value.length);
2332 fixed (char *dest = tmp, src = this, val = value) {
2334 CharCopy (dst, src, startIndex);
2336 CharCopy (dst, val, value.length);
2337 dst += value.length;
2338 CharCopy (dst, src + startIndex, length - startIndex);
2343 public static string Intern (string str)
2346 throw new ArgumentNullException ("str");
2348 return InternalIntern (str);
2351 public static string IsInterned (string str)
2354 throw new ArgumentNullException ("str");
2356 return InternalIsInterned (str);
2360 public static string Join (string separator, params string [] value)
2362 public static string Join (string separator, string [] value)
2366 throw new ArgumentNullException ("value");
2367 if (separator == null)
2370 return JoinUnchecked (separator, value, 0, value.Length);
2373 public static string Join (string separator, string[] value, int startIndex, int count)
2376 throw new ArgumentNullException ("value");
2378 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2380 throw new ArgumentOutOfRangeException ("count", "< 0");
2381 if (startIndex > value.Length - count)
2382 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2384 if (startIndex == value.Length)
2386 if (separator == null)
2389 return JoinUnchecked (separator, value, startIndex, count);
2392 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2394 // Unchecked parameters
2395 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2396 // separator and value must not be null
2399 int maxIndex = startIndex + count;
2400 // Precount the number of characters that the resulting string will have
2401 for (int i = startIndex; i < maxIndex; i++) {
2402 String s = value[i];
2406 length += separator.length * (count - 1);
2410 String tmp = InternalAllocateStr (length);
2413 fixed (char* dest = tmp, sepsrc = separator) {
2414 // Copy each string from value except the last one and add a separator for each
2416 for (int i = startIndex; i < maxIndex; i++) {
2417 String source = value[i];
2418 if (source != null) {
2419 if (source.Length > 0) {
2420 fixed (char* src = source)
2421 CharCopy (dest + pos, src, source.Length);
2422 pos += source.Length;
2425 if (separator.Length > 0) {
2426 CharCopy (dest + pos, sepsrc, separator.Length);
2427 pos += separator.Length;
2430 // Append last string that does not get an additional separator
2431 String sourceLast = value[maxIndex];
2432 if (sourceLast != null) {
2433 if (sourceLast.Length > 0) {
2434 fixed (char* src = sourceLast)
2435 CharCopy (dest + pos, src, sourceLast.Length);
2442 bool IConvertible.ToBoolean (IFormatProvider provider)
2444 return Convert.ToBoolean (this, provider);
2447 byte IConvertible.ToByte (IFormatProvider provider)
2449 return Convert.ToByte (this, provider);
2452 char IConvertible.ToChar (IFormatProvider provider)
2454 return Convert.ToChar (this, provider);
2457 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2459 return Convert.ToDateTime (this, provider);
2462 decimal IConvertible.ToDecimal (IFormatProvider provider)
2464 return Convert.ToDecimal (this, provider);
2467 double IConvertible.ToDouble (IFormatProvider provider)
2469 return Convert.ToDouble (this, provider);
2472 short IConvertible.ToInt16 (IFormatProvider provider)
2474 return Convert.ToInt16 (this, provider);
2477 int IConvertible.ToInt32 (IFormatProvider provider)
2479 return Convert.ToInt32 (this, provider);
2482 long IConvertible.ToInt64 (IFormatProvider provider)
2484 return Convert.ToInt64 (this, provider);
2487 sbyte IConvertible.ToSByte (IFormatProvider provider)
2489 return Convert.ToSByte (this, provider);
2492 float IConvertible.ToSingle (IFormatProvider provider)
2494 return Convert.ToSingle (this, provider);
2497 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2499 if (targetType == null)
2500 throw new ArgumentNullException ("type");
2501 return Convert.ToType (this, targetType, provider, false);
2504 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2506 return Convert.ToUInt16 (this, provider);
2509 uint IConvertible.ToUInt32 (IFormatProvider provider)
2511 return Convert.ToUInt32 (this, provider);
2514 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2516 return Convert.ToUInt64 (this, provider);
2525 public CharEnumerator GetEnumerator ()
2527 return new CharEnumerator (this);
2530 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2532 return new CharEnumerator (this);
2535 IEnumerator IEnumerable.GetEnumerator ()
2537 return new CharEnumerator (this);
2540 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2541 out bool left_align, out string format)
2543 int max = str.Length;
2545 // parses format specifier of form:
2549 // N = argument number (non-negative integer)
2551 n = ParseDecimal (str, ref ptr);
2553 throw new FormatException ("Input string was not in a correct format.");
2555 // M = width (non-negative integer)
2557 if (ptr < max && str[ptr] == ',') {
2558 // White space between ',' and number or sign.
2560 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2564 format = str.Substring (start, ptr - start);
2566 left_align = (ptr < max && str [ptr] == '-');
2570 width = ParseDecimal (str, ref ptr);
2572 throw new FormatException ("Input string was not in a correct format.");
2580 // F = argument format (string)
2582 if (ptr < max && str[ptr] == ':') {
2584 while (ptr < max && str[ptr] != '}')
2587 format += str.Substring (start, ptr - start);
2592 if ((ptr >= max) || str[ptr ++] != '}')
2593 throw new FormatException ("Input string was not in a correct format.");
2596 private static int ParseDecimal (string str, ref int ptr)
2600 int max = str.Length;
2604 if (c < '0' || '9' < c)
2607 n = n * 10 + c - '0';
2611 if (p == ptr || p == max)
2618 internal unsafe void InternalSetChar (int idx, char val)
2620 if ((uint) idx >= (uint) Length)
2621 throw new ArgumentOutOfRangeException ("idx");
2623 fixed (char * pStr = &start_char)
2629 internal unsafe void InternalSetLength (int newLength)
2631 if (newLength > length)
2632 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2634 // zero terminate, we can pass string objects directly via pinvoke
2635 // we also zero the rest of the string, since the new GC needs to be
2636 // able to handle the changing size (it will skip the 0 bytes).
2637 fixed (char * pStr = &start_char) {
2638 char *p = pStr + newLength;
2639 char *end = pStr + length;
2648 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2649 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2650 public unsafe override int GetHashCode ()
2652 fixed (char * c = this) {
2654 char * end = cc + length - 1;
2656 for (;cc < end; cc += 2) {
2657 h = (h << 5) - h + *cc;
2658 h = (h << 5) - h + cc [1];
2662 h = (h << 5) - h + *cc;
2669 public static string Concat (IEnumerable<string> values)
2672 throw new ArgumentNullException ("values");
2674 var stringList = new List<string> ();
2676 foreach (var v in values){
2682 return ConcatInternal (stringList.ToArray (), len);
2685 [ComVisibleAttribute(false)]
2686 public static string Concat<T> (IEnumerable<T> values)
2689 throw new ArgumentNullException ("values");
2691 var stringList = new List<string> ();
2693 foreach (var v in values){
2694 string sr = v.ToString ();
2696 stringList.Add (sr);
2698 return ConcatInternal (stringList.ToArray (), len);
2701 [ComVisibleAttribute(false)]
2702 public static string Join (string separator, IEnumerable<string> values)
2704 if (separator == null)
2705 return Concat (values);
2708 throw new ArgumentNullException ("values");
2710 var stringList = new List<string> ();
2711 foreach (var v in values)
2714 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2717 [ComVisibleAttribute(false)]
2718 public static string Join (string separator, params object [] values)
2720 if (separator == null)
2721 return Concat (values);
2724 throw new ArgumentNullException ("values");
2726 var strCopy = new string [values.Length];
2728 foreach (var v in values)
2729 strCopy [i++] = v.ToString ();
2731 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2734 [ComVisible (false)]
2735 public static string Join<T> (string separator, IEnumerable<T> values)
2737 if (separator == null)
2738 return Concat<T> (values);
2741 throw new ArgumentNullException ("values");
2743 var stringList = new List<string> ();
2744 foreach (var v in values)
2745 stringList.Add (v.ToString ());
2747 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2750 public static bool IsNullOrWhiteSpace (string value)
2752 internal static bool IsNullOrWhiteSpace (string value)
2755 if ((value == null) || (value.Length == 0))
2757 foreach (char c in value)
2758 if (!Char.IsWhiteSpace (c))
2763 internal unsafe int GetCaseInsensitiveHashCode ()
2765 fixed (char * c = this) {
2767 char * end = cc + length - 1;
2769 for (;cc < end; cc += 2) {
2770 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2771 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2775 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2780 // Certain constructors are redirected to CreateString methods with
2781 // matching argument list. The this pointer should not be used.
2783 private unsafe String CreateString (sbyte* value)
2788 byte* bytes = (byte*) value;
2792 while (bytes++ [0] != 0)
2794 } catch (NullReferenceException) {
2795 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2798 return CreateString (value, 0, length, null);
2801 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2803 return CreateString (value, startIndex, length, null);
2806 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2809 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2811 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2812 if (value + startIndex < value)
2813 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2817 throw new ArgumentNullException ("value");
2821 enc = Encoding.Default;
2824 byte [] bytes = new byte [length];
2827 fixed (byte* bytePtr = bytes)
2829 memcpy (bytePtr, (byte*) (value + startIndex), length);
2830 } catch (NullReferenceException) {
2831 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2834 // GetString () is called even when length == 0
2835 return enc.GetString (bytes);
2838 unsafe string CreateString (char *value)
2848 string result = InternalAllocateStr (i);
2851 fixed (char *dest = result) {
2852 CharCopy (dest, value, i);
2858 unsafe string CreateString (char *value, int startIndex, int length)
2863 throw new ArgumentNullException ("value");
2865 throw new ArgumentOutOfRangeException ("startIndex");
2867 throw new ArgumentOutOfRangeException ("length");
2869 string result = InternalAllocateStr (length);
2871 fixed (char *dest = result) {
2872 CharCopy (dest, value + startIndex, length);
2877 unsafe string CreateString (char [] val, int startIndex, int length)
2880 throw new ArgumentNullException ("value");
2882 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2884 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2885 if (startIndex > val.Length - length)
2886 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2890 string result = InternalAllocateStr (length);
2892 fixed (char *dest = result, src = val) {
2893 CharCopy (dest, src + startIndex, length);
2898 unsafe string CreateString (char [] val)
2900 if (val == null || val.Length == 0)
2902 string result = InternalAllocateStr (val.Length);
2904 fixed (char *dest = result, src = val) {
2905 CharCopy (dest, src, val.Length);
2910 unsafe string CreateString (char c, int count)
2913 throw new ArgumentOutOfRangeException ("count");
2916 string result = InternalAllocateStr (count);
2917 fixed (char *dest = result) {
2919 char *end = p + count;
2928 /* helpers used by the runtime as well as above or eslewhere in corlib */
2929 internal static unsafe void memset (byte *dest, int val, int len)
2940 val = val | (val << 8);
2941 val = val | (val << 16);
2944 int rest = (int)dest & 3;
2952 } while (rest != 0);
2955 ((int*)dest) [0] = val;
2956 ((int*)dest) [1] = val;
2957 ((int*)dest) [2] = val;
2958 ((int*)dest) [3] = val;
2963 ((int*)dest) [0] = val;
2975 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2976 /*while (size >= 32) {
2977 // using long is better than int and slower than double
2978 // FIXME: enable this only on correct alignment or on platforms
2979 // that can tolerate unaligned reads/writes of doubles
2980 ((double*)dest) [0] = ((double*)src) [0];
2981 ((double*)dest) [1] = ((double*)src) [1];
2982 ((double*)dest) [2] = ((double*)src) [2];
2983 ((double*)dest) [3] = ((double*)src) [3];
2988 while (size >= 16) {
2989 ((int*)dest) [0] = ((int*)src) [0];
2990 ((int*)dest) [1] = ((int*)src) [1];
2991 ((int*)dest) [2] = ((int*)src) [2];
2992 ((int*)dest) [3] = ((int*)src) [3];
2998 ((int*)dest) [0] = ((int*)src) [0];
3004 ((byte*)dest) [0] = ((byte*)src) [0];
3010 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3012 ((short*)dest) [0] = ((short*)src) [0];
3013 ((short*)dest) [1] = ((short*)src) [1];
3014 ((short*)dest) [2] = ((short*)src) [2];
3015 ((short*)dest) [3] = ((short*)src) [3];
3021 ((short*)dest) [0] = ((short*)src) [0];
3027 ((byte*)dest) [0] = ((byte*)src) [0];
3029 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3031 ((byte*)dest) [0] = ((byte*)src) [0];
3032 ((byte*)dest) [1] = ((byte*)src) [1];
3033 ((byte*)dest) [2] = ((byte*)src) [2];
3034 ((byte*)dest) [3] = ((byte*)src) [3];
3035 ((byte*)dest) [4] = ((byte*)src) [4];
3036 ((byte*)dest) [5] = ((byte*)src) [5];
3037 ((byte*)dest) [6] = ((byte*)src) [6];
3038 ((byte*)dest) [7] = ((byte*)src) [7];
3044 ((byte*)dest) [0] = ((byte*)src) [0];
3045 ((byte*)dest) [1] = ((byte*)src) [1];
3051 ((byte*)dest) [0] = ((byte*)src) [0];
3054 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3055 // FIXME: if pointers are not aligned, try to align them
3056 // so a faster routine can be used. Handle the case where
3057 // the pointers can't be reduced to have the same alignment
3058 // (just ignore the issue on x86?)
3059 if ((((int)dest | (int)src) & 3) != 0) {
3060 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3066 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3067 ((short*)dest) [0] = ((short*)src) [0];
3072 if ((((int)dest | (int)src) & 1) != 0) {
3073 memcpy1 (dest, src, size);
3076 if ((((int)dest | (int)src) & 2) != 0) {
3077 memcpy2 (dest, src, size);
3081 memcpy4 (dest, src, size);
3084 /* Used by the runtime */
3085 internal static unsafe void bzero (byte *dest, int len) {
3086 memset (dest, 0, len);
3089 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3090 ((byte*)dest) [0] = 0;
3093 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3094 ((short*)dest) [0] = 0;
3097 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3098 ((int*)dest) [0] = 0;
3101 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3102 ((long*)dest) [0] = 0;
3105 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3106 ((byte*)dest) [0] = ((byte*)src) [0];
3109 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3110 ((short*)dest) [0] = ((short*)src) [0];
3113 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3114 ((int*)dest) [0] = ((int*)src) [0];
3117 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3118 ((long*)dest) [0] = ((long*)src) [0];
3121 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3122 // Same rules as for memcpy, but with the premise that
3123 // chars can only be aligned to even addresses if their
3124 // enclosing types are correctly aligned
3125 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3126 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3127 ((short*)dest) [0] = ((short*)src) [0];
3132 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3133 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3137 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3140 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3144 for (int i = count; i > 0; i--) {
3151 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3153 fixed (char* dest = target, src = source)
3154 CharCopy (dest + targetIndex, src + sourceIndex, count);
3157 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3159 fixed (char* dest = target, src = source)
3160 CharCopy (dest + targetIndex, src + sourceIndex, count);
3163 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3164 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3166 fixed (char* dest = target, src = source)
3167 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3170 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3171 unsafe public extern String (char *value);
3173 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3174 unsafe public extern String (char *value, int startIndex, int length);
3176 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3177 unsafe public extern String (sbyte *value);
3179 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3180 unsafe public extern String (sbyte *value, int startIndex, int length);
3182 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3183 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3185 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3186 public extern String (char [] value, int startIndex, int length);
3188 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3189 public extern String (char [] value);
3191 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3192 public extern String (char c, int count);
3194 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3195 internal extern static String InternalAllocateStr (int length);
3197 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3198 private extern static string InternalIntern (string str);
3200 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3201 private extern static string InternalIsInterned (string str);
3203 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3204 private extern static int GetLOSLimit ();