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.None);
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)
1520 throw new ArgumentNullException ("value");
1522 return IndexOfOrdinalUnchecked (value, 0, Length) != -1;
1525 public static bool IsNullOrEmpty (String value)
1527 return (value == null) || (value.Length == 0);
1530 public string Normalize ()
1532 return Normalization.Normalize (this, 0);
1535 public string Normalize (NormalizationForm normalizationForm)
1537 switch (normalizationForm) {
1539 return Normalization.Normalize (this, 0);
1540 case NormalizationForm.FormD:
1541 return Normalization.Normalize (this, 1);
1542 case NormalizationForm.FormKC:
1543 return Normalization.Normalize (this, 2);
1544 case NormalizationForm.FormKD:
1545 return Normalization.Normalize (this, 3);
1549 public bool IsNormalized ()
1551 return Normalization.IsNormalized (this, 0);
1554 public bool IsNormalized (NormalizationForm normalizationForm)
1556 switch (normalizationForm) {
1558 return Normalization.IsNormalized (this, 0);
1559 case NormalizationForm.FormD:
1560 return Normalization.IsNormalized (this, 1);
1561 case NormalizationForm.FormKC:
1562 return Normalization.IsNormalized (this, 2);
1563 case NormalizationForm.FormKD:
1564 return Normalization.IsNormalized (this, 3);
1568 public string Remove (int startIndex)
1571 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1572 if (startIndex >= this.length)
1573 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1575 return Remove (startIndex, this.length - startIndex);
1578 public String PadLeft (int totalWidth)
1580 return PadLeft (totalWidth, ' ');
1583 public unsafe String PadLeft (int totalWidth, char paddingChar)
1585 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1588 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1590 if (totalWidth < this.length)
1592 if (totalWidth == 0)
1595 String tmp = InternalAllocateStr (totalWidth);
1597 fixed (char* dest = tmp, src = this) {
1598 char* padPos = dest;
1601 padTo = checked (dest + (totalWidth - length));
1602 } catch (OverflowException) {
1603 throw new OutOfMemoryException ();
1606 while (padPos != padTo)
1607 *padPos++ = paddingChar;
1609 CharCopy (padTo, src, length);
1614 public String PadRight (int totalWidth)
1616 return PadRight (totalWidth, ' ');
1619 public unsafe String PadRight (int totalWidth, char paddingChar)
1621 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1624 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1626 if (totalWidth < this.length)
1628 if (totalWidth == 0)
1631 String tmp = InternalAllocateStr (totalWidth);
1633 fixed (char* dest = tmp, src = this) {
1634 CharCopy (dest, src, length);
1637 char* padPos = checked (dest + length);
1638 char* padTo = checked (dest + totalWidth);
1639 while (padPos != padTo)
1640 *padPos++ = paddingChar;
1641 } catch (OverflowException) {
1642 throw new OutOfMemoryException ();
1648 public bool StartsWith (String value)
1651 throw new ArgumentNullException ("value");
1653 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1656 [ComVisible (false)]
1657 public bool StartsWith (string value, StringComparison comparisonType)
1660 throw new ArgumentNullException ("value");
1662 switch (comparisonType) {
1663 case StringComparison.CurrentCulture:
1664 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1665 case StringComparison.CurrentCultureIgnoreCase:
1666 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1667 case StringComparison.InvariantCulture:
1668 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1669 case StringComparison.InvariantCultureIgnoreCase:
1670 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1671 case StringComparison.Ordinal:
1672 return StartsWithOrdinalUnchecked (value);
1673 case StringComparison.OrdinalIgnoreCase:
1674 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1676 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1677 throw new ArgumentException (msg, "comparisonType");
1681 internal bool StartsWithOrdinalUnchecked (string value)
1683 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1686 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1688 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1691 [ComVisible (false)]
1692 public bool EndsWith (string value, StringComparison comparisonType)
1695 throw new ArgumentNullException ("value");
1697 switch (comparisonType) {
1698 case StringComparison.CurrentCulture:
1699 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1700 case StringComparison.CurrentCultureIgnoreCase:
1701 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1702 case StringComparison.InvariantCulture:
1703 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1704 case StringComparison.InvariantCultureIgnoreCase:
1705 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1706 case StringComparison.Ordinal:
1707 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1708 case StringComparison.OrdinalIgnoreCase:
1709 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1711 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1712 throw new ArgumentException (msg, "comparisonType");
1716 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1718 if (culture == null)
1719 culture = CultureInfo.CurrentCulture;
1721 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1724 // Following method is culture-insensitive
1725 public unsafe String Replace (char oldChar, char newChar)
1727 if (this.length == 0 || oldChar == newChar)
1730 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1731 if (start_pos == -1)
1737 string tmp = InternalAllocateStr (length);
1738 fixed (char* dest = tmp, src = &start_char) {
1740 CharCopy (dest, src, start_pos);
1742 char* end_ptr = dest + length;
1743 char* dest_ptr = dest + start_pos;
1744 char* src_ptr = src + start_pos;
1746 while (dest_ptr != end_ptr) {
1747 if (*src_ptr == oldChar)
1748 *dest_ptr = newChar;
1750 *dest_ptr = *src_ptr;
1759 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1760 public String Replace (String oldValue, String newValue)
1762 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1763 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1765 if (oldValue == null)
1766 throw new ArgumentNullException ("oldValue");
1768 if (oldValue.Length == 0)
1769 throw new ArgumentException ("oldValue is the empty string.");
1771 if (this.Length == 0)
1774 if (newValue == null)
1777 return ReplaceUnchecked (oldValue, newValue);
1780 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1782 if (oldValue.length > length)
1784 if (oldValue.length == 1 && newValue.length == 1) {
1785 return Replace (oldValue[0], newValue[0]);
1786 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1787 // because the length of the result would be this.length and length calculation unneccesary
1790 const int maxValue = 200; // Allocate 800 byte maximum
1791 int* dat = stackalloc int[maxValue];
1792 fixed (char* source = this, replace = newValue) {
1793 int i = 0, count = 0;
1794 while (i < length) {
1795 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1799 if (count < maxValue)
1800 dat[count++] = found;
1802 return ReplaceFallback (oldValue, newValue, maxValue);
1804 i = found + oldValue.length;
1811 nlen = this.length + ((newValue.length - oldValue.length) * count);
1812 } catch (OverflowException) {
1813 throw new OutOfMemoryException ();
1816 String tmp = InternalAllocateStr (nlen);
1818 int curPos = 0, lastReadPos = 0;
1819 fixed (char* dest = tmp) {
1820 for (int j = 0; j < count; j++) {
1821 int precopy = dat[j] - lastReadPos;
1822 CharCopy (dest + curPos, source + lastReadPos, precopy);
1824 lastReadPos = dat[j] + oldValue.length;
1825 CharCopy (dest + curPos, replace, newValue.length);
1826 curPos += newValue.length;
1828 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1834 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1836 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1837 StringBuilder sb = new StringBuilder (lengthEstimate);
1838 for (int i = 0; i < length;) {
1839 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1841 sb.Append (SubstringUnchecked (i, length - i));
1844 sb.Append (SubstringUnchecked (i, found - i));
1845 sb.Append (newValue);
1846 i = found + oldValue.Length;
1848 return sb.ToString ();
1852 public unsafe String Remove (int startIndex, int count)
1855 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1857 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1858 if (startIndex > this.length - count)
1859 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1861 String tmp = InternalAllocateStr (this.length - count);
1863 fixed (char *dest = tmp, src = this) {
1865 CharCopy (dst, src, startIndex);
1866 int skip = startIndex + count;
1868 CharCopy (dst, src + skip, length - skip);
1873 public String ToLower ()
1875 return ToLower (CultureInfo.CurrentCulture);
1878 public String ToLower (CultureInfo culture)
1880 if (culture == null)
1881 throw new ArgumentNullException ("culture");
1883 if (culture.LCID == 0x007F) // Invariant
1884 return ToLowerInvariant ();
1886 return culture.TextInfo.ToLower (this);
1889 public unsafe String ToLowerInvariant ()
1894 string tmp = InternalAllocateStr (length);
1895 fixed (char* source = &start_char, dest = tmp) {
1897 char* destPtr = (char*)dest;
1898 char* sourcePtr = (char*)source;
1900 for (int n = 0; n < length; n++) {
1901 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1909 public String ToUpper ()
1911 return ToUpper (CultureInfo.CurrentCulture);
1914 public String ToUpper (CultureInfo culture)
1916 if (culture == null)
1917 throw new ArgumentNullException ("culture");
1919 if (culture.LCID == 0x007F) // Invariant
1920 return ToUpperInvariant ();
1922 return culture.TextInfo.ToUpper (this);
1925 public unsafe String ToUpperInvariant ()
1930 string tmp = InternalAllocateStr (length);
1931 fixed (char* source = &start_char, dest = tmp) {
1933 char* destPtr = (char*)dest;
1934 char* sourcePtr = (char*)source;
1936 for (int n = 0; n < length; n++) {
1937 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1945 public override String ToString ()
1950 public String ToString (IFormatProvider provider)
1955 public static String Format (String format, Object arg0)
1957 return Format (null, format, new Object[] {arg0});
1960 public static String Format (String format, Object arg0, Object arg1)
1962 return Format (null, format, new Object[] {arg0, arg1});
1965 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1967 return Format (null, format, new Object[] {arg0, arg1, arg2});
1970 public static string Format (string format, params object[] args)
1972 return Format (null, format, args);
1975 public static string Format (IFormatProvider provider, string format, params object[] args)
1977 StringBuilder b = FormatHelper (null, provider, format, args);
1978 return b.ToString ();
1981 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1984 throw new ArgumentNullException ("format");
1986 throw new ArgumentNullException ("args");
1988 if (result == null) {
1989 /* Try to approximate the size of result to avoid reallocations */
1993 for (i = 0; i < args.Length; ++i) {
1994 string s = args [i] as string;
2000 if (i == args.Length)
2001 result = new StringBuilder (len + format.length);
2003 result = new StringBuilder ();
2008 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
2010 while (ptr < format.length) {
2011 char c = format[ptr ++];
2014 result.Append (format, start, ptr - start - 1);
2016 // check for escaped open bracket
2018 if (format[ptr] == '{') {
2029 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2030 if (n >= args.Length)
2031 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2035 object arg = args[n];
2040 else if (formatter != null)
2041 str = formatter.Format (arg_format, arg, provider);
2046 if (arg is IFormattable)
2047 str = ((IFormattable)arg).ToString (arg_format, provider);
2049 str = arg.ToString () ?? Empty;
2052 // pad formatted string and append to result
2053 if (width > str.length) {
2054 const char padchar = ' ';
2055 int padlen = width - str.length;
2058 result.Append (str);
2059 result.Append (padchar, padlen);
2062 result.Append (padchar, padlen);
2063 result.Append (str);
2066 result.Append (str);
2071 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2072 result.Append (format, start, ptr - start - 1);
2075 else if (c == '}') {
2076 throw new FormatException ("Input string was not in a correct format.");
2080 if (start < format.length)
2081 result.Append (format, start, format.Length - start);
2086 public unsafe static String Copy (String str)
2089 throw new ArgumentNullException ("str");
2091 int length = str.length;
2093 String tmp = InternalAllocateStr (length);
2095 fixed (char *dest = tmp, src = str) {
2096 CharCopy (dest, src, length);
2102 public static String Concat (Object arg0)
2107 return arg0.ToString ();
2110 public static String Concat (Object arg0, Object arg1)
2112 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2115 public static String Concat (Object arg0, Object arg1, Object arg2)
2121 s1 = arg0.ToString ();
2126 s2 = arg1.ToString ();
2131 s3 = arg2.ToString ();
2133 return Concat (s1, s2, s3);
2136 [CLSCompliant(false)]
2137 public static String Concat (Object arg0, Object arg1, Object arg2,
2138 Object arg3, __arglist)
2140 string s1, s2, s3, s4;
2145 s1 = arg0.ToString ();
2150 s2 = arg1.ToString ();
2155 s3 = arg2.ToString ();
2157 ArgIterator iter = new ArgIterator (__arglist);
2158 int argCount = iter.GetRemainingCount();
2160 StringBuilder sb = new StringBuilder ();
2162 sb.Append (arg3.ToString ());
2164 for (int i = 0; i < argCount; i++) {
2165 TypedReference typedRef = iter.GetNextArg ();
2166 sb.Append (TypedReference.ToObject (typedRef));
2169 s4 = sb.ToString ();
2171 return Concat (s1, s2, s3, s4);
2174 public unsafe static String Concat (String str0, String str1)
2176 if (str0 == null || str0.Length == 0) {
2177 if (str1 == null || str1.Length == 0)
2182 if (str1 == null || str1.Length == 0)
2185 int nlen = str0.length + str1.length;
2187 throw new OutOfMemoryException ();
2188 String tmp = InternalAllocateStr (nlen);
2190 fixed (char *dest = tmp, src = str0)
2191 CharCopy (dest, src, str0.length);
2192 fixed (char *dest = tmp, src = str1)
2193 CharCopy (dest + str0.Length, src, str1.length);
2198 public unsafe static String Concat (String str0, String str1, String str2)
2200 if (str0 == null || str0.Length == 0){
2201 if (str1 == null || str1.Length == 0){
2202 if (str2 == null || str2.Length == 0)
2206 if (str2 == null || str2.Length == 0)
2211 if (str1 == null || str1.Length == 0){
2212 if (str2 == null || str2.Length == 0)
2217 if (str2 == null || str2.Length == 0)
2222 int nlen = str0.length + str1.length;
2224 throw new OutOfMemoryException ();
2225 nlen += str2.length;
2227 throw new OutOfMemoryException ();
2228 String tmp = InternalAllocateStr (nlen);
2230 if (str0.Length != 0) {
2231 fixed (char *dest = tmp, src = str0) {
2232 CharCopy (dest, src, str0.length);
2235 if (str1.Length != 0) {
2236 fixed (char *dest = tmp, src = str1) {
2237 CharCopy (dest + str0.Length, src, str1.length);
2240 if (str2.Length != 0) {
2241 fixed (char *dest = tmp, src = str2) {
2242 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2249 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2251 if (str0 == null && str1 == null && str2 == null && str3 == null)
2263 int nlen = str0.length + str1.length;
2265 throw new OutOfMemoryException ();
2266 nlen += str2.length;
2268 throw new OutOfMemoryException ();
2269 nlen += str3.length;
2271 throw new OutOfMemoryException ();
2272 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2274 if (str0.Length != 0) {
2275 fixed (char *dest = tmp, src = str0) {
2276 CharCopy (dest, src, str0.length);
2279 if (str1.Length != 0) {
2280 fixed (char *dest = tmp, src = str1) {
2281 CharCopy (dest + str0.Length, src, str1.length);
2284 if (str2.Length != 0) {
2285 fixed (char *dest = tmp, src = str2) {
2286 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2289 if (str3.Length != 0) {
2290 fixed (char *dest = tmp, src = str3) {
2291 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2298 public static String Concat (params Object[] args)
2301 throw new ArgumentNullException ("args");
2303 int argLen = args.Length;
2307 string [] strings = new string [argLen];
2309 for (int i = 0; i < argLen; i++) {
2310 if (args[i] != null) {
2311 strings[i] = args[i].ToString ();
2312 len += strings[i].length;
2314 throw new OutOfMemoryException ();
2318 return ConcatInternal (strings, len);
2321 public static String Concat (params String[] values)
2324 throw new ArgumentNullException ("values");
2327 for (int i = 0; i < values.Length; i++) {
2328 String s = values[i];
2332 throw new OutOfMemoryException ();
2335 return ConcatInternal (values, len);
2338 private static unsafe String ConcatInternal (String[] values, int length)
2343 throw new OutOfMemoryException ();
2345 String tmp = InternalAllocateStr (length);
2347 fixed (char* dest = tmp) {
2349 for (int i = 0; i < values.Length; i++) {
2350 String source = values[i];
2351 if (source != null) {
2352 fixed (char* src = source) {
2353 CharCopy (dest + pos, src, source.length);
2355 pos += source.Length;
2362 public unsafe String Insert (int startIndex, String value)
2365 throw new ArgumentNullException ("value");
2367 if (startIndex < 0 || startIndex > this.length)
2368 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2370 if (value.Length == 0)
2372 if (this.Length == 0)
2375 int nlen = this.length + value.length;
2377 throw new OutOfMemoryException ();
2379 String tmp = InternalAllocateStr (nlen);
2381 fixed (char *dest = tmp, src = this, val = value) {
2383 CharCopy (dst, src, startIndex);
2385 CharCopy (dst, val, value.length);
2386 dst += value.length;
2387 CharCopy (dst, src + startIndex, length - startIndex);
2392 public static string Intern (string str)
2395 throw new ArgumentNullException ("str");
2397 return InternalIntern (str);
2400 public static string IsInterned (string str)
2403 throw new ArgumentNullException ("str");
2405 return InternalIsInterned (str);
2409 public static string Join (string separator, params string [] value)
2411 public static string Join (string separator, string [] value)
2415 throw new ArgumentNullException ("value");
2416 if (separator == null)
2419 return JoinUnchecked (separator, value, 0, value.Length);
2422 public static string Join (string separator, string[] value, int startIndex, int count)
2425 throw new ArgumentNullException ("value");
2427 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2429 throw new ArgumentOutOfRangeException ("count", "< 0");
2430 if (startIndex > value.Length - count)
2431 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2433 if (startIndex == value.Length)
2435 if (separator == null)
2438 return JoinUnchecked (separator, value, startIndex, count);
2441 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2443 // Unchecked parameters
2444 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2445 // separator and value must not be null
2448 int maxIndex = startIndex + count;
2449 // Precount the number of characters that the resulting string will have
2450 for (int i = startIndex; i < maxIndex; i++) {
2451 String s = value[i];
2455 length += separator.length * (count - 1);
2459 String tmp = InternalAllocateStr (length);
2462 fixed (char* dest = tmp, sepsrc = separator) {
2463 // Copy each string from value except the last one and add a separator for each
2465 for (int i = startIndex; i < maxIndex; i++) {
2466 String source = value[i];
2467 if (source != null) {
2468 if (source.Length > 0) {
2469 fixed (char* src = source)
2470 CharCopy (dest + pos, src, source.Length);
2471 pos += source.Length;
2474 if (separator.Length > 0) {
2475 CharCopy (dest + pos, sepsrc, separator.Length);
2476 pos += separator.Length;
2479 // Append last string that does not get an additional separator
2480 String sourceLast = value[maxIndex];
2481 if (sourceLast != null) {
2482 if (sourceLast.Length > 0) {
2483 fixed (char* src = sourceLast)
2484 CharCopy (dest + pos, src, sourceLast.Length);
2491 bool IConvertible.ToBoolean (IFormatProvider provider)
2493 return Convert.ToBoolean (this, provider);
2496 byte IConvertible.ToByte (IFormatProvider provider)
2498 return Convert.ToByte (this, provider);
2501 char IConvertible.ToChar (IFormatProvider provider)
2503 return Convert.ToChar (this, provider);
2506 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2508 return Convert.ToDateTime (this, provider);
2511 decimal IConvertible.ToDecimal (IFormatProvider provider)
2513 return Convert.ToDecimal (this, provider);
2516 double IConvertible.ToDouble (IFormatProvider provider)
2518 return Convert.ToDouble (this, provider);
2521 short IConvertible.ToInt16 (IFormatProvider provider)
2523 return Convert.ToInt16 (this, provider);
2526 int IConvertible.ToInt32 (IFormatProvider provider)
2528 return Convert.ToInt32 (this, provider);
2531 long IConvertible.ToInt64 (IFormatProvider provider)
2533 return Convert.ToInt64 (this, provider);
2536 sbyte IConvertible.ToSByte (IFormatProvider provider)
2538 return Convert.ToSByte (this, provider);
2541 float IConvertible.ToSingle (IFormatProvider provider)
2543 return Convert.ToSingle (this, provider);
2546 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2548 if (targetType == null)
2549 throw new ArgumentNullException ("type");
2550 return Convert.ToType (this, targetType, provider, false);
2553 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2555 return Convert.ToUInt16 (this, provider);
2558 uint IConvertible.ToUInt32 (IFormatProvider provider)
2560 return Convert.ToUInt32 (this, provider);
2563 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2565 return Convert.ToUInt64 (this, provider);
2574 public CharEnumerator GetEnumerator ()
2576 return new CharEnumerator (this);
2579 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2581 return new CharEnumerator (this);
2584 IEnumerator IEnumerable.GetEnumerator ()
2586 return new CharEnumerator (this);
2589 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2590 out bool left_align, out string format)
2592 int max = str.Length;
2594 // parses format specifier of form:
2598 // N = argument number (non-negative integer)
2600 n = ParseDecimal (str, ref ptr);
2602 throw new FormatException ("Input string was not in a correct format.");
2604 // M = width (non-negative integer)
2606 if (ptr < max && str[ptr] == ',') {
2607 // White space between ',' and number or sign.
2609 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2613 format = str.Substring (start, ptr - start);
2615 left_align = (ptr < max && str [ptr] == '-');
2619 width = ParseDecimal (str, ref ptr);
2621 throw new FormatException ("Input string was not in a correct format.");
2629 // F = argument format (string)
2631 if (ptr < max && str[ptr] == ':') {
2633 while (ptr < max && str[ptr] != '}')
2636 format += str.Substring (start, ptr - start);
2641 if ((ptr >= max) || str[ptr ++] != '}')
2642 throw new FormatException ("Input string was not in a correct format.");
2645 private static int ParseDecimal (string str, ref int ptr)
2649 int max = str.Length;
2653 if (c < '0' || '9' < c)
2656 n = n * 10 + c - '0';
2660 if (p == ptr || p == max)
2667 internal unsafe void InternalSetChar (int idx, char val)
2669 if ((uint) idx >= (uint) Length)
2670 throw new ArgumentOutOfRangeException ("idx");
2672 fixed (char * pStr = &start_char)
2678 internal unsafe void InternalSetLength (int newLength)
2680 if (newLength > length)
2681 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2683 // zero terminate, we can pass string objects directly via pinvoke
2684 // we also zero the rest of the string, since the new GC needs to be
2685 // able to handle the changing size (it will skip the 0 bytes).
2686 fixed (char * pStr = &start_char) {
2687 char *p = pStr + newLength;
2688 char *end = pStr + length;
2697 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2698 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2699 public unsafe override int GetHashCode ()
2701 fixed (char * c = this) {
2703 char * end = cc + length - 1;
2705 for (;cc < end; cc += 2) {
2706 h = (h << 5) - h + *cc;
2707 h = (h << 5) - h + cc [1];
2711 h = (h << 5) - h + *cc;
2718 public static string Concat (IEnumerable<string> values)
2721 throw new ArgumentNullException ("values");
2723 var stringList = new List<string> ();
2725 foreach (var v in values){
2730 throw new OutOfMemoryException ();
2733 return ConcatInternal (stringList.ToArray (), len);
2736 [ComVisibleAttribute(false)]
2737 public static string Concat<T> (IEnumerable<T> values)
2740 throw new ArgumentNullException ("values");
2742 var stringList = new List<string> ();
2744 foreach (var v in values){
2745 string sr = v.ToString ();
2748 throw new OutOfMemoryException ();
2749 stringList.Add (sr);
2751 return ConcatInternal (stringList.ToArray (), len);
2754 [ComVisibleAttribute(false)]
2755 public static string Join (string separator, IEnumerable<string> values)
2757 if (separator == null)
2758 return Concat (values);
2761 throw new ArgumentNullException ("values");
2763 var stringList = new List<string> (values);
2765 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2768 [ComVisibleAttribute(false)]
2769 public static string Join (string separator, params object [] values)
2771 if (separator == null)
2772 return Concat (values);
2775 throw new ArgumentNullException ("values");
2777 var strCopy = new string [values.Length];
2779 foreach (var v in values)
2780 strCopy [i++] = v.ToString ();
2782 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2785 [ComVisible (false)]
2786 public static string Join<T> (string separator, IEnumerable<T> values)
2788 if (separator == null)
2789 return Concat<T> (values);
2792 throw new ArgumentNullException ("values");
2794 var stringList = values as IList<T> ?? new List<T> (values);
2795 var strCopy = new string [stringList.Count];
2797 foreach (var v in stringList)
2798 strCopy [i++] = v.ToString ();
2800 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2803 public static bool IsNullOrWhiteSpace (string value)
2805 internal static bool IsNullOrWhiteSpace (string value)
2808 if ((value == null) || (value.Length == 0))
2810 foreach (char c in value)
2811 if (!Char.IsWhiteSpace (c))
2816 internal unsafe int GetCaseInsensitiveHashCode ()
2818 fixed (char * c = this) {
2820 char * end = cc + length - 1;
2822 for (;cc < end; cc += 2) {
2823 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2824 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2828 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2833 // Certain constructors are redirected to CreateString methods with
2834 // matching argument list. The this pointer should not be used.
2836 private unsafe String CreateString (sbyte* value)
2841 byte* bytes = (byte*) value;
2845 while (bytes++ [0] != 0)
2847 } catch (NullReferenceException) {
2848 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2851 return CreateString (value, 0, length, null);
2854 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2856 return CreateString (value, startIndex, length, null);
2859 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2862 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2864 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2865 if (value + startIndex < value)
2866 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2870 throw new ArgumentNullException ("value");
2874 enc = Encoding.Default;
2877 byte [] bytes = new byte [length];
2880 fixed (byte* bytePtr = bytes)
2883 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2884 memcpy (bytePtr, (byte*) (value + startIndex), length);
2885 } catch (NullReferenceException) {
2886 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2889 // GetString () is called even when length == 0
2890 return enc.GetString (bytes);
2893 unsafe string CreateString (char *value)
2903 string result = InternalAllocateStr (i);
2906 fixed (char *dest = result) {
2907 CharCopy (dest, value, i);
2913 unsafe string CreateString (char *value, int startIndex, int length)
2918 throw new ArgumentNullException ("value");
2920 throw new ArgumentOutOfRangeException ("startIndex");
2922 throw new ArgumentOutOfRangeException ("length");
2924 string result = InternalAllocateStr (length);
2926 fixed (char *dest = result) {
2927 CharCopy (dest, value + startIndex, length);
2932 unsafe string CreateString (char [] val, int startIndex, int length)
2935 throw new ArgumentNullException ("value");
2937 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2939 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2940 if (startIndex > val.Length - length)
2941 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2945 string result = InternalAllocateStr (length);
2947 fixed (char *dest = result, src = val) {
2948 CharCopy (dest, src + startIndex, length);
2953 unsafe string CreateString (char [] val)
2955 if (val == null || val.Length == 0)
2957 string result = InternalAllocateStr (val.Length);
2959 fixed (char *dest = result, src = val) {
2960 CharCopy (dest, src, val.Length);
2965 unsafe string CreateString (char c, int count)
2968 throw new ArgumentOutOfRangeException ("count");
2971 string result = InternalAllocateStr (count);
2972 fixed (char *dest = result) {
2974 char *end = p + count;
2983 /* helpers used by the runtime as well as above or eslewhere in corlib */
2984 internal static unsafe void memset (byte *dest, int val, int len)
2995 val = val | (val << 8);
2996 val = val | (val << 16);
2999 int rest = (int)dest & 3;
3007 } while (rest != 0);
3010 ((int*)dest) [0] = val;
3011 ((int*)dest) [1] = val;
3012 ((int*)dest) [2] = val;
3013 ((int*)dest) [3] = val;
3018 ((int*)dest) [0] = val;
3030 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
3031 /*while (size >= 32) {
3032 // using long is better than int and slower than double
3033 // FIXME: enable this only on correct alignment or on platforms
3034 // that can tolerate unaligned reads/writes of doubles
3035 ((double*)dest) [0] = ((double*)src) [0];
3036 ((double*)dest) [1] = ((double*)src) [1];
3037 ((double*)dest) [2] = ((double*)src) [2];
3038 ((double*)dest) [3] = ((double*)src) [3];
3043 while (size >= 16) {
3044 ((int*)dest) [0] = ((int*)src) [0];
3045 ((int*)dest) [1] = ((int*)src) [1];
3046 ((int*)dest) [2] = ((int*)src) [2];
3047 ((int*)dest) [3] = ((int*)src) [3];
3053 ((int*)dest) [0] = ((int*)src) [0];
3059 ((byte*)dest) [0] = ((byte*)src) [0];
3065 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3067 ((short*)dest) [0] = ((short*)src) [0];
3068 ((short*)dest) [1] = ((short*)src) [1];
3069 ((short*)dest) [2] = ((short*)src) [2];
3070 ((short*)dest) [3] = ((short*)src) [3];
3076 ((short*)dest) [0] = ((short*)src) [0];
3082 ((byte*)dest) [0] = ((byte*)src) [0];
3084 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3086 ((byte*)dest) [0] = ((byte*)src) [0];
3087 ((byte*)dest) [1] = ((byte*)src) [1];
3088 ((byte*)dest) [2] = ((byte*)src) [2];
3089 ((byte*)dest) [3] = ((byte*)src) [3];
3090 ((byte*)dest) [4] = ((byte*)src) [4];
3091 ((byte*)dest) [5] = ((byte*)src) [5];
3092 ((byte*)dest) [6] = ((byte*)src) [6];
3093 ((byte*)dest) [7] = ((byte*)src) [7];
3099 ((byte*)dest) [0] = ((byte*)src) [0];
3100 ((byte*)dest) [1] = ((byte*)src) [1];
3106 ((byte*)dest) [0] = ((byte*)src) [0];
3109 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3110 // FIXME: if pointers are not aligned, try to align them
3111 // so a faster routine can be used. Handle the case where
3112 // the pointers can't be reduced to have the same alignment
3113 // (just ignore the issue on x86?)
3114 if ((((int)dest | (int)src) & 3) != 0) {
3115 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3121 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3122 ((short*)dest) [0] = ((short*)src) [0];
3127 if ((((int)dest | (int)src) & 1) != 0) {
3128 memcpy1 (dest, src, size);
3131 if ((((int)dest | (int)src) & 2) != 0) {
3132 memcpy2 (dest, src, size);
3136 memcpy4 (dest, src, size);
3139 /* Used by the runtime */
3140 internal static unsafe void bzero (byte *dest, int len) {
3141 memset (dest, 0, len);
3144 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3145 ((byte*)dest) [0] = 0;
3148 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3149 ((short*)dest) [0] = 0;
3152 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3153 ((int*)dest) [0] = 0;
3156 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3157 ((long*)dest) [0] = 0;
3160 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3161 ((byte*)dest) [0] = ((byte*)src) [0];
3164 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3165 ((short*)dest) [0] = ((short*)src) [0];
3168 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3169 ((int*)dest) [0] = ((int*)src) [0];
3172 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3173 ((long*)dest) [0] = ((long*)src) [0];
3176 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3177 // Same rules as for memcpy, but with the premise that
3178 // chars can only be aligned to even addresses if their
3179 // enclosing types are correctly aligned
3180 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3181 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3182 ((short*)dest) [0] = ((short*)src) [0];
3187 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3188 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3192 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3195 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3199 for (int i = count; i > 0; i--) {
3206 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3208 fixed (char* dest = target, src = source)
3209 CharCopy (dest + targetIndex, src + sourceIndex, count);
3212 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3214 fixed (char* dest = target, src = source)
3215 CharCopy (dest + targetIndex, src + sourceIndex, count);
3218 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3219 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3221 fixed (char* dest = target, src = source)
3222 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3225 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3226 unsafe public extern String (char *value);
3228 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3229 unsafe public extern String (char *value, int startIndex, int length);
3231 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3232 unsafe public extern String (sbyte *value);
3234 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3235 unsafe public extern String (sbyte *value, int startIndex, int length);
3237 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3238 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3240 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3241 public extern String (char [] value, int startIndex, int length);
3243 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3244 public extern String (char [] value);
3246 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3247 public extern String (char c, int count);
3249 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3250 internal extern static String InternalAllocateStr (int length);
3252 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3253 private extern static string InternalIntern (string str);
3255 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3256 private extern static string InternalIsInterned (string str);
3258 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3259 private extern static int GetLOSLimit ();