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)
11 // (C) 2001 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
40 using System.Collections.Generic;
41 using System.Runtime.ConstrainedExecution;
42 using System.Runtime.InteropServices;
43 using Mono.Globalization.Unicode;
51 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
53 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
56 [NonSerialized] private int length;
57 [NonSerialized] private char start_char;
59 private const int COMPARE_CASE = 0;
60 private const int COMPARE_INCASE = 1;
61 private const int COMPARE_ORDINAL = 2;
63 public static readonly String Empty = "";
65 public static unsafe bool Equals (string a, string b)
67 if ((a as object) == (b as object))
70 if (a == null || b == null)
78 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
83 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
84 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
85 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
86 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
95 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
96 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
105 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
113 return len == 0 || *s1_ptr == *s2_ptr;
117 public static bool operator == (String a, String b)
119 return Equals (a, b);
122 public static bool operator != (String a, String b)
124 return !Equals (a, b);
128 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
130 public override bool Equals (Object obj)
132 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public bool Equals (String value)
140 return Equals (this, value);
143 [IndexerName ("Chars")]
144 public extern char this [int index] {
145 [MethodImplAttribute (MethodImplOptions.InternalCall)]
149 public Object Clone ()
154 public TypeCode GetTypeCode ()
156 return TypeCode.String;
159 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
161 // LAMESPEC: should I null-terminate?
162 if (destination == null)
163 throw new ArgumentNullException ("destination");
165 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
166 throw new ArgumentOutOfRangeException ();
168 // re-ordered to avoid possible integer overflow
169 if (sourceIndex > Length - count)
170 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
171 // re-ordered to avoid possible integer overflow
172 if (destinationIndex > destination.Length - count)
173 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
175 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
178 public char[] ToCharArray ()
180 return ToCharArray (0, length);
183 public char[] ToCharArray (int startIndex, int length)
186 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
188 throw new ArgumentOutOfRangeException ("length", "< 0");
189 // re-ordered to avoid possible integer overflow
190 if (startIndex > this.length - length)
191 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
193 char[] tmp = new char [length];
195 InternalCopyTo (startIndex, tmp, 0, length);
200 public String [] Split (params char [] separator)
202 return Split (separator, Int32.MaxValue);
205 public String[] Split (char[] separator, int count)
207 if (separator == null || separator.Length == 0)
208 separator = WhiteChars;
211 throw new ArgumentOutOfRangeException ("count");
214 return new String[0];
217 return new String[1] { ToString() };
219 return InternalSplit (separator, count);
224 [MonoDocumentationNote ("optimization")]
225 public String[] Split (char[] separator, int count, StringSplitOptions options)
227 if (separator == null || separator.Length == 0)
228 return Split (WhiteChars, count, options);
231 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
232 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
233 throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
235 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
238 return Split (separator, count);
240 /* FIXME: Optimize this */
241 String[] res = Split (separator, count);
243 for (int i = 0; i < res.Length; ++i)
244 if (res [i] == String.Empty)
247 String[] arr = new String [res.Length - n];
249 for (int i = 0; i < res.Length; ++i)
250 if (res [i] != String.Empty)
251 arr [pos ++] = res [i];
260 public String[] Split (string[] separator, int count, StringSplitOptions options)
262 if (separator == null || separator.Length == 0)
263 return Split (WhiteChars, count, options);
266 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
267 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
268 throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
270 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
272 if (count == 0 || (this == String.Empty && removeEmpty))
273 return new String [0];
275 ArrayList arr = new ArrayList ();
278 while (pos < this.Length) {
280 int matchPos = Int32.MaxValue;
282 // Find the first position where any of the separators matches
283 for (int i = 0; i < separator.Length; ++i) {
284 string sep = separator [i];
285 if (sep == null || sep == String.Empty)
288 int match = IndexOf (sep, pos);
289 if (match > -1 && match < matchPos) {
295 if (matchIndex == -1)
298 if (matchPos == pos && removeEmpty) {
299 pos = matchPos + separator [matchIndex].Length;
302 arr.Add (this.Substring (pos, matchPos - pos));
304 pos = matchPos + separator [matchIndex].Length;
306 if (arr.Count == count - 1) {
313 return new String [] { this };
315 if (removeEmpty && pos == this.Length) {
316 String[] res = new String [arr.Count];
317 arr.CopyTo (0, res, 0, arr.Count);
322 String[] res = new String [arr.Count + 1];
323 arr.CopyTo (0, res, 0, arr.Count);
324 res [arr.Count] = this.Substring (pos);
332 public String[] Split (char[] separator, StringSplitOptions options)
334 return Split (separator, Int32.MaxValue, options);
338 public String[] Split (String[] separator, StringSplitOptions options)
340 return Split (separator, Int32.MaxValue, options);
344 public unsafe String Substring (int startIndex)
349 if (startIndex < 0 || startIndex > this.length)
350 throw new ArgumentOutOfRangeException ("startIndex");
352 int newlen = this.length - startIndex;
353 string tmp = InternalAllocateStr (newlen);
355 fixed (char *dest = tmp, src = this) {
356 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
362 public unsafe String Substring (int startIndex, int length)
365 throw new ArgumentOutOfRangeException ("length", "< 0");
367 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
368 // re-ordered to avoid possible integer overflow
369 if (startIndex > this.length - length)
370 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
375 string tmp = InternalAllocateStr (length);
376 fixed (char *dest = tmp, src = this) {
377 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
383 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
385 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
387 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
388 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
389 (char) 0x3000, (char) 0xFEFF };
391 public String Trim ()
393 return InternalTrim (WhiteChars, 0);
396 public String Trim (params char[] trimChars)
398 if (trimChars == null || trimChars.Length == 0)
399 trimChars = WhiteChars;
401 return InternalTrim (trimChars, 0);
404 public String TrimStart (params char[] trimChars)
406 if (trimChars == null || trimChars.Length == 0)
407 trimChars = WhiteChars;
409 return InternalTrim (trimChars, 1);
412 public String TrimEnd (params char[] trimChars)
414 if (trimChars == null || trimChars.Length == 0)
415 trimChars = WhiteChars;
417 return InternalTrim (trimChars, 2);
420 public static int Compare (String strA, String strB)
422 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
425 public static int Compare (String strA, String strB, bool ignoreCase)
427 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
430 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
433 throw new ArgumentNullException ("culture");
442 else if (strB == null) {
446 CompareOptions compopts;
449 compopts = CompareOptions.IgnoreCase;
451 compopts = CompareOptions.None;
453 return culture.CompareInfo.Compare (strA, strB, compopts);
456 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
458 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
461 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
463 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
466 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
469 throw new ArgumentNullException ("culture");
471 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
472 throw new ArgumentOutOfRangeException ();
484 else if (strB == null) {
488 CompareOptions compopts;
491 compopts = CompareOptions.IgnoreCase;
493 compopts = CompareOptions.None;
495 /* Need to cap the requested length to the
496 * length of the string, because
497 * CompareInfo.Compare will insist that length
498 * <= (string.Length - offset)
503 if (length > (strA.Length - indexA)) {
504 len1 = strA.Length - indexA;
507 if (length > (strB.Length - indexB)) {
508 len2 = strB.Length - indexB;
511 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
514 public static int Compare (string strA, string strB, StringComparison comparisonType)
516 switch (comparisonType) {
517 case StringComparison.CurrentCulture:
518 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
519 case StringComparison.CurrentCultureIgnoreCase:
520 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
521 case StringComparison.InvariantCulture:
522 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
523 case StringComparison.InvariantCultureIgnoreCase:
524 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
525 case StringComparison.Ordinal:
526 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
527 case StringComparison.OrdinalIgnoreCase:
528 return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
530 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
531 throw new ArgumentException ("comparisonType", msg);
535 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
537 switch (comparisonType) {
538 case StringComparison.CurrentCulture:
539 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
540 case StringComparison.CurrentCultureIgnoreCase:
541 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
542 case StringComparison.InvariantCulture:
543 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
544 case StringComparison.InvariantCultureIgnoreCase:
545 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
546 case StringComparison.Ordinal:
547 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
548 case StringComparison.OrdinalIgnoreCase:
549 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
551 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
552 throw new ArgumentException ("comparisonType", msg);
556 public static bool Equals (string a, string b, StringComparison comparisonType)
558 return String.Compare (a, b, comparisonType) == 0;
561 public bool Equals (string value, StringComparison comparisonType)
563 return String.Equals (this, value, comparisonType);
566 public int CompareTo (Object value)
571 if (!(value is String))
572 throw new ArgumentException ();
574 return String.Compare (this, (String) value, false);
577 public int CompareTo (String strB)
582 return Compare (this, strB, false);
585 public static unsafe int CompareOrdinal (String strA, String strB)
592 } else if (strB == null) {
595 fixed (char* aptr = strA, bptr = strB) {
597 char* end = ap + Math.Min (strA.Length, strB.Length);
605 return strA.Length - strB.Length;
609 internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
616 } else if (strB == null) {
620 /* Invariant, because that is cheaper to
621 * instantiate (and chances are it already has
624 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
627 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
629 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
632 internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
634 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
635 throw new ArgumentOutOfRangeException ();
643 else if (strB == null) {
647 /* Need to cap the requested length to the
648 * length of the string, because
649 * CompareInfo.Compare will insist that length
650 * <= (string.Length - offset)
655 if (length > (strA.Length - indexA)) {
656 len1 = strA.Length - indexA;
659 if (length > (strB.Length - indexB)) {
660 len2 = strB.Length - indexB;
663 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
666 public bool EndsWith (String value)
668 return EndsWith (value, false, CultureInfo.CurrentCulture);
676 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
678 return (culture.CompareInfo.IsSuffix (this, value,
679 ignoreCase ? CompareOptions.IgnoreCase :
680 CompareOptions.None));
683 public int IndexOfAny (char [] anyOf)
686 throw new ArgumentNullException ("anyOf");
687 if (this.length == 0)
690 return InternalIndexOfAny (anyOf, 0, this.length);
693 public int IndexOfAny (char [] anyOf, int startIndex)
696 throw new ArgumentNullException ("anyOf");
697 if (startIndex < 0 || startIndex > this.length)
698 throw new ArgumentOutOfRangeException ("startIndex");
700 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
703 public int IndexOfAny (char [] anyOf, int startIndex, int count)
706 throw new ArgumentNullException ("anyOf");
708 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
710 throw new ArgumentOutOfRangeException ("count", "< 0");
711 // re-ordered to avoid possible integer overflow
712 if (startIndex > this.length - count)
713 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
715 return InternalIndexOfAny (anyOf, startIndex, count);
718 unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
720 if (anyOf.Length == 0)
723 if (anyOf.Length == 1)
724 return IndexOfImpl(anyOf[0], startIndex, count);
726 fixed (char* any = anyOf) {
730 char* end_any_ptr = any + anyOf.Length;
732 while (++any_ptr != end_any_ptr) {
733 if (*any_ptr > highest) {
738 if (*any_ptr < lowest)
742 fixed (char* start = &start_char) {
743 char* ptr = start + startIndex;
744 char* end_ptr = ptr + count;
746 while (ptr != end_ptr) {
747 if (*ptr > highest || *ptr < lowest) {
753 return (int)(ptr - start);
756 while (++any_ptr != end_any_ptr) {
757 if (*ptr == *any_ptr)
758 return (int)(ptr - start);
770 public int IndexOf (string value, StringComparison comparison)
772 return IndexOf (value, 0, this.Length, comparison);
775 public int IndexOf (string value, int startIndex, StringComparison comparison)
777 return IndexOf (value, startIndex, this.Length - startIndex, comparison);
780 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
782 switch (comparison) {
783 case StringComparison.CurrentCulture:
784 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
785 case StringComparison.CurrentCultureIgnoreCase:
786 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
787 case StringComparison.InvariantCulture:
788 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
789 case StringComparison.InvariantCultureIgnoreCase:
790 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
791 case StringComparison.Ordinal:
792 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
793 case StringComparison.OrdinalIgnoreCase:
794 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
796 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
799 public int LastIndexOf (string value, StringComparison comparison)
801 return LastIndexOf (value, value.Length - 1, value.Length, comparison);
804 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
806 return LastIndexOf (value, startIndex, startIndex + 1, comparison);
809 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
811 switch (comparison) {
812 case StringComparison.CurrentCulture:
813 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
814 case StringComparison.CurrentCultureIgnoreCase:
815 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
816 case StringComparison.InvariantCulture:
817 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
818 case StringComparison.InvariantCultureIgnoreCase:
819 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
820 case StringComparison.Ordinal:
821 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
822 case StringComparison.OrdinalIgnoreCase:
823 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
825 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
829 public int IndexOf (char value)
831 if (this.length == 0)
834 return IndexOfImpl (value, 0, this.length);
837 public int IndexOf (String value)
839 return IndexOf (value, 0, this.length);
842 public int IndexOf (char value, int startIndex)
844 return IndexOf (value, startIndex, this.length - startIndex);
847 public int IndexOf (String value, int startIndex)
849 return IndexOf (value, startIndex, this.length - startIndex);
852 /* This method is culture-insensitive */
853 public int IndexOf (char value, int startIndex, int count)
856 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
858 throw new ArgumentOutOfRangeException ("count", "< 0");
859 // re-ordered to avoid possible integer overflow
860 if (startIndex > this.length - count)
861 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
863 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
866 return IndexOfImpl (value, startIndex, count);
869 unsafe int IndexOfImpl (char value, int startIndex, int count)
871 // It helps JIT compiler to optimize comparison
872 int value_32 = (int)value;
874 fixed (char* start = &start_char) {
875 char* ptr = start + startIndex;
876 char* end_ptr = ptr + (count >> 3 << 3);
878 while (ptr != end_ptr) {
879 if (*ptr == value_32)
880 return (int)(ptr - start);
881 if (ptr[1] == value_32)
882 return (int)(ptr - start + 1);
883 if (ptr[2] == value_32)
884 return (int)(ptr - start + 2);
885 if (ptr[3] == value_32)
886 return (int)(ptr - start + 3);
887 if (ptr[4] == value_32)
888 return (int)(ptr - start + 4);
889 if (ptr[5] == value_32)
890 return (int)(ptr - start + 5);
891 if (ptr[6] == value_32)
892 return (int)(ptr - start + 6);
893 if (ptr[7] == value_32)
894 return (int)(ptr - start + 7);
899 end_ptr += count & 0x07;
900 while (ptr != end_ptr) {
901 if (*ptr == value_32)
902 return (int)(ptr - start);
910 /* But this one is culture-sensitive */
911 public int IndexOf (String value, int startIndex, int count)
914 throw new ArgumentNullException ("value");
916 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
918 throw new ArgumentOutOfRangeException ("count", "< 0");
919 // re-ordered to avoid possible integer overflow
920 if (startIndex > this.length - count)
921 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
923 if (value.length == 0)
926 if (startIndex == 0 && this.length == 0)
932 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
935 public int LastIndexOfAny (char [] anyOf)
938 throw new ArgumentNullException ("anyOf");
940 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
943 public int LastIndexOfAny (char [] anyOf, int startIndex)
946 throw new ArgumentNullException ("anyOf");
948 if (startIndex < 0 || startIndex >= this.length)
949 throw new ArgumentOutOfRangeException ();
951 if (this.length == 0)
954 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
957 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
960 throw new ArgumentNullException ("anyOf");
962 if ((startIndex < 0) || (startIndex >= this.Length))
963 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
964 if ((count < 0) || (count > this.Length))
965 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
966 if (startIndex - count + 1 < 0)
967 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
969 if (this.length == 0)
972 return InternalLastIndexOfAny (anyOf, startIndex, count);
975 public int LastIndexOf (char value)
977 if (this.length == 0)
980 return LastIndexOfImpl (value, this.length - 1, this.length);
983 public int LastIndexOf (String value)
985 if (this.length == 0)
986 /* This overload does additional checking */
987 return LastIndexOf (value, 0, 0);
989 return LastIndexOf (value, this.length - 1, this.length);
992 public int LastIndexOf (char value, int startIndex)
994 return LastIndexOf (value, startIndex, startIndex + 1);
997 public int LastIndexOf (String value, int startIndex)
1000 throw new ArgumentNullException ("value");
1001 int max = startIndex;
1002 if (max < this.Length)
1004 return LastIndexOf (value, startIndex, max);
1007 /* This method is culture-insensitive */
1008 public int LastIndexOf (char value, int startIndex, int count)
1010 if (startIndex == 0 && this.length == 0)
1013 // >= for char (> for string)
1014 if ((startIndex < 0) || (startIndex >= this.Length))
1015 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1016 if ((count < 0) || (count > this.Length))
1017 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1018 if (startIndex - count + 1 < 0)
1019 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1021 return LastIndexOfImpl (value, startIndex, count);
1024 /* This method is culture-insensitive */
1025 unsafe int LastIndexOfImpl (char value, int startIndex, int count)
1027 // It helps JIT compiler to optimize comparison
1028 int value_32 = (int)value;
1030 fixed (char* start = &start_char) {
1031 char* ptr = start + startIndex;
1032 char* end_ptr = ptr - (count >> 3 << 3);
1034 while (ptr != end_ptr) {
1035 if (*ptr == value_32)
1036 return (int)(ptr - start);
1037 if (ptr[-1] == value_32)
1038 return (int)(ptr - start) - 1;
1039 if (ptr[-2] == value_32)
1040 return (int)(ptr - start) - 2;
1041 if (ptr[-3] == value_32)
1042 return (int)(ptr - start) - 3;
1043 if (ptr[-4] == value_32)
1044 return (int)(ptr - start) - 4;
1045 if (ptr[-5] == value_32)
1046 return (int)(ptr - start) - 5;
1047 if (ptr[-6] == value_32)
1048 return (int)(ptr - start) - 6;
1049 if (ptr[-7] == value_32)
1050 return (int)(ptr - start) - 7;
1055 end_ptr -= count & 0x07;
1056 while (ptr != end_ptr) {
1057 if (*ptr == value_32)
1058 return (int)(ptr - start);
1066 /* But this one is culture-sensitive */
1067 public int LastIndexOf (String value, int startIndex, int count)
1070 throw new ArgumentNullException ("value");
1071 // -1 > startIndex > for string (0 > startIndex >= for char)
1072 if ((startIndex < -1) || (startIndex > this.Length))
1073 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1074 if ((count < 0) || (count > this.Length))
1075 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1076 if (startIndex - count + 1 < 0)
1077 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1079 if (value.Length == 0)
1082 if (startIndex == 0 && this.length == 0)
1085 // This check is needed to match undocumented MS behaviour
1086 if (this.length == 0 && value.length > 0)
1092 if (startIndex == this.Length)
1094 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1098 public bool Contains (String value)
1100 return IndexOf (value) != -1;
1103 public static bool IsNullOrEmpty (String value)
1105 return (value == null) || (value.Length == 0);
1108 public string Normalize ()
1110 return Normalize (NormalizationForm.FormC);
1113 public string Normalize (NormalizationForm form)
1117 return Normalization.Normalize (this, 0);
1118 case NormalizationForm.FormD:
1119 return Normalization.Normalize (this, 1);
1120 case NormalizationForm.FormKC:
1121 return Normalization.Normalize (this, 2);
1122 case NormalizationForm.FormKD:
1123 return Normalization.Normalize (this, 3);
1127 public bool IsNormalized ()
1129 return IsNormalized (NormalizationForm.FormC);
1132 public bool IsNormalized (NormalizationForm form)
1136 return Normalization.IsNormalized (this, 0);
1137 case NormalizationForm.FormD:
1138 return Normalization.IsNormalized (this, 1);
1139 case NormalizationForm.FormKC:
1140 return Normalization.IsNormalized (this, 2);
1141 case NormalizationForm.FormKD:
1142 return Normalization.IsNormalized (this, 3);
1146 public string Remove (int startIndex)
1149 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1150 if (startIndex >= this.length)
1151 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1153 return Remove (startIndex, this.length - startIndex);
1157 public String PadLeft (int totalWidth)
1159 return PadLeft (totalWidth, ' ');
1162 public String PadLeft (int totalWidth, char paddingChar)
1165 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1167 if (totalWidth < this.length)
1168 return String.Copy (this);
1170 return InternalPad (totalWidth, paddingChar, false);
1173 public String PadRight (int totalWidth)
1175 return PadRight (totalWidth, ' ');
1178 public String PadRight (int totalWidth, char paddingChar)
1181 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1183 if (totalWidth < this.length)
1184 return String.Copy (this);
1186 return InternalPad (totalWidth, paddingChar, true);
1189 public bool StartsWith (String value)
1191 return StartsWith (value, false, CultureInfo.CurrentCulture);
1195 [ComVisible (false)]
1196 public bool StartsWith (string value, StringComparison comparisonType)
1198 switch (comparisonType) {
1199 case StringComparison.CurrentCulture:
1200 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1201 case StringComparison.CurrentCultureIgnoreCase:
1202 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1203 case StringComparison.InvariantCulture:
1204 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1205 case StringComparison.InvariantCultureIgnoreCase:
1206 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1207 case StringComparison.Ordinal:
1208 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1209 case StringComparison.OrdinalIgnoreCase:
1210 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1216 [ComVisible (false)]
1217 public bool EndsWith (string value, StringComparison comparisonType)
1219 switch (comparisonType) {
1220 case StringComparison.CurrentCulture:
1221 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1222 case StringComparison.CurrentCultureIgnoreCase:
1223 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1224 case StringComparison.InvariantCulture:
1225 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1226 case StringComparison.InvariantCultureIgnoreCase:
1227 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1228 case StringComparison.Ordinal:
1229 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1230 case StringComparison.OrdinalIgnoreCase:
1231 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1244 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1246 if (culture == null)
1247 culture = CultureInfo.CurrentCulture;
1249 return (culture.CompareInfo.IsPrefix (this, value,
1250 ignoreCase ? CompareOptions.IgnoreCase :
1251 CompareOptions.None));
1254 /* This method is culture insensitive */
1255 public unsafe String Replace (char oldChar, char newChar)
1257 if (this.length == 0 || oldChar == newChar)
1260 int start_pos = IndexOfImpl (oldChar, 0, this.length);
1261 if (start_pos == -1)
1267 string tmp = InternalAllocateStr(length);
1268 fixed (char* dest = tmp, src = &start_char) {
1270 memcpy((byte*)dest, (byte*)src, start_pos * 2);
1272 char* end_ptr = dest + length;
1273 char* dest_ptr = dest + start_pos;
1274 char* src_ptr = src + start_pos;
1276 while (dest_ptr != end_ptr) {
1277 if (*src_ptr == oldChar)
1278 *dest_ptr = newChar;
1280 *dest_ptr = *src_ptr;
1289 /* This method is culture sensitive */
1290 public String Replace (String oldValue, String newValue)
1292 if (oldValue == null)
1293 throw new ArgumentNullException ("oldValue");
1295 if (oldValue.Length == 0)
1296 throw new ArgumentException ("oldValue is the empty string.");
1298 if (this.Length == 0)
1301 if (newValue == null)
1302 newValue = String.Empty;
1304 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1307 public unsafe String Remove (int startIndex, int count)
1310 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1312 throw new ArgumentOutOfRangeException ("count", "< 0");
1313 // re-ordered to avoid possible integer overflow
1314 if (startIndex > this.length - count)
1315 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
1317 String tmp = InternalAllocateStr (this.length - count);
1319 fixed (char *dest = tmp, src = this) {
1321 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1322 int skip = startIndex + count;
1324 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
1329 public String ToLower ()
1331 return ToLower (CultureInfo.CurrentCulture);
1334 public String ToLower (CultureInfo culture)
1336 if (culture == null)
1337 throw new ArgumentNullException ("culture");
1339 if (culture.LCID == 0x007F) { // Invariant
1340 return ToLowerInvariant ();
1342 return culture.TextInfo.ToLower (this);
1346 public unsafe String ToLowerInvariant ()
1348 internal unsafe String ToLowerInvariant ()
1351 string tmp = InternalAllocateStr (length);
1352 fixed (char* source = &start_char, dest = tmp) {
1354 char* destPtr = (char*)dest;
1355 char* sourcePtr = (char*)source;
1357 for (int n = 0; n < length; n++) {
1358 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1366 public String ToUpper ()
1368 return ToUpper (CultureInfo.CurrentCulture);
1371 public String ToUpper (CultureInfo culture)
1373 if (culture == null)
1374 throw new ArgumentNullException ("culture");
1376 if (culture.LCID == 0x007F) { // Invariant
1377 return ToUpperInvariant ();
1379 return culture.TextInfo.ToUpper (this);
1383 public unsafe String ToUpperInvariant ()
1385 internal unsafe String ToUpperInvariant ()
1388 string tmp = InternalAllocateStr (length);
1389 fixed (char* source = &start_char, dest = tmp) {
1391 char* destPtr = (char*)dest;
1392 char* sourcePtr = (char*)source;
1394 for (int n = 0; n < length; n++) {
1395 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1403 public override String ToString ()
1408 public String ToString (IFormatProvider provider)
1413 public static String Format (String format, Object arg0)
1415 return Format (null, format, new Object[] {arg0});
1418 public static String Format (String format, Object arg0, Object arg1)
1420 return Format (null, format, new Object[] {arg0, arg1});
1423 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1425 return Format (null, format, new Object[] {arg0, arg1, arg2});
1428 public static string Format (string format, params object[] args)
1430 return Format (null, format, args);
1433 public static string Format (IFormatProvider provider, string format, params object[] args)
1435 StringBuilder b = new StringBuilder ();
1436 FormatHelper (b, provider, format, args);
1437 return b.ToString ();
1440 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1442 if (format == null || args == null)
1443 throw new ArgumentNullException ();
1447 while (ptr < format.length) {
1448 char c = format[ptr ++];
1451 result.Append (format, start, ptr - start - 1);
1453 // check for escaped open bracket
1455 if (format[ptr] == '{') {
1466 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1467 if (n >= args.Length)
1468 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1472 object arg = args[n];
1475 ICustomFormatter formatter = null;
1476 if (provider != null)
1477 formatter = provider.GetFormat (typeof (ICustomFormatter))
1478 as ICustomFormatter;
1481 else if (formatter != null)
1482 str = formatter.Format (arg_format, arg, provider);
1483 else if (arg is IFormattable)
1484 str = ((IFormattable)arg).ToString (arg_format, provider);
1486 str = arg.ToString ();
1488 // pad formatted string and append to result
1490 if (width > str.length) {
1491 const char padchar = ' ';
1492 int padlen = width - str.length;
1495 result.Append (str);
1496 result.Append (padchar, padlen);
1499 result.Append (padchar, padlen);
1500 result.Append (str);
1504 result.Append (str);
1508 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1509 result.Append (format, start, ptr - start - 1);
1512 else if (c == '}') {
1513 throw new FormatException ("Input string was not in a correct format.");
1517 if (start < format.length)
1518 result.Append (format, start, format.Length - start);
1521 public unsafe static String Copy (String str)
1524 throw new ArgumentNullException ("str");
1526 int length = str.length;
1528 String tmp = InternalAllocateStr (length);
1530 fixed (char *dest = tmp, src = str) {
1531 memcpy ((byte*)dest, (byte*)src, length * 2);
1537 public static String Concat (Object obj)
1540 return String.Empty;
1542 return obj.ToString ();
1545 public unsafe static String Concat (Object obj1, Object obj2)
1549 s1 = (obj1 != null) ? obj1.ToString () : null;
1550 s2 = (obj2 != null) ? obj2.ToString () : null;
1554 return String.Empty;
1557 } else if (s2 == null)
1560 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1561 if (s1.Length != 0) {
1562 fixed (char *dest = tmp, src = s1) {
1563 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1566 if (s2.Length != 0) {
1567 fixed (char *dest = tmp, src = s2) {
1568 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1575 public static String Concat (Object obj1, Object obj2, Object obj3)
1581 s1 = obj1.ToString ();
1586 s2 = obj2.ToString ();
1591 s3 = obj3.ToString ();
1593 return Concat (s1, s2, s3);
1596 #if ! BOOTSTRAP_WITH_OLDLIB
1597 [CLSCompliant(false)]
1598 public static String Concat (Object obj1, Object obj2, Object obj3,
1599 Object obj4, __arglist)
1601 string s1, s2, s3, s4;
1606 s1 = obj1.ToString ();
1611 s2 = obj2.ToString ();
1616 s3 = obj3.ToString ();
1618 ArgIterator iter = new ArgIterator (__arglist);
1619 int argCount = iter.GetRemainingCount();
1621 StringBuilder sb = new StringBuilder ();
1623 sb.Append (obj4.ToString ());
1625 for (int i = 0; i < argCount; i++) {
1626 TypedReference typedRef = iter.GetNextArg ();
1627 sb.Append (TypedReference.ToObject (typedRef));
1630 s4 = sb.ToString ();
1632 return Concat (s1, s2, s3, s4);
1636 public unsafe static String Concat (String s1, String s2)
1638 if (s1 == null || s1.Length == 0) {
1639 if (s2 == null || s2.Length == 0)
1640 return String.Empty;
1644 if (s2 == null || s2.Length == 0)
1647 String tmp = InternalAllocateStr (s1.length + s2.length);
1649 if (s1.Length != 0) {
1650 fixed (char *dest = tmp, src = s1) {
1651 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1654 if (s2.Length != 0) {
1655 fixed (char *dest = tmp, src = s2) {
1656 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1663 public unsafe static String Concat (String s1, String s2, String s3)
1665 if (s1 == null || s1.Length == 0){
1666 if (s2 == null || s2.Length == 0){
1667 if (s3 == null || s3.Length == 0)
1668 return String.Empty;
1671 if (s3 == null || s3.Length == 0)
1676 if (s2 == null || s2.Length == 0){
1677 if (s3 == null || s3.Length == 0)
1682 if (s3 == null || s3.Length == 0)
1687 //return InternalConcat (s1, s2, s3);
1688 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1690 if (s1.Length != 0) {
1691 fixed (char *dest = tmp, src = s1) {
1692 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1695 if (s2.Length != 0) {
1696 fixed (char *dest = tmp, src = s2) {
1697 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1700 if (s3.Length != 0) {
1701 fixed (char *dest = tmp, src = s3) {
1702 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1709 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1711 if (s1 == null && s2 == null && s3 == null && s4 == null)
1712 return String.Empty;
1723 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1725 if (s1.Length != 0) {
1726 fixed (char *dest = tmp, src = s1) {
1727 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1730 if (s2.Length != 0) {
1731 fixed (char *dest = tmp, src = s2) {
1732 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1735 if (s3.Length != 0) {
1736 fixed (char *dest = tmp, src = s3) {
1737 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1740 if (s4.Length != 0) {
1741 fixed (char *dest = tmp, src = s4) {
1742 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1749 public static String Concat (params Object[] args)
1752 throw new ArgumentNullException ("args");
1754 int i = args.Length;
1756 return String.Empty;
1758 string [] strings = new string [i];
1761 foreach (object arg in args) {
1763 strings[i] = String.Empty;
1765 strings[i] = arg.ToString ();
1766 len += strings[i].length;
1772 return String.Empty;
1774 return InternalJoin (String.Empty, strings, 0, strings.Length);
1777 public static String Concat (params String[] values)
1780 throw new ArgumentNullException ("values");
1782 return InternalJoin (String.Empty, values, 0, values.Length);
1785 public unsafe String Insert (int startIndex, String value)
1788 throw new ArgumentNullException ("value");
1790 if (startIndex < 0 || startIndex > this.length)
1791 throw new ArgumentOutOfRangeException ();
1793 if (value.Length == 0)
1795 if (this.Length == 0)
1797 String tmp = InternalAllocateStr (this.length + value.length);
1799 fixed (char *dest = tmp, src = this, val = value) {
1801 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1803 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1804 dst += value.length;
1805 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1811 public static string Intern (string str)
1814 throw new ArgumentNullException ("str");
1816 return InternalIntern (str);
1819 public static string IsInterned (string str)
1822 throw new ArgumentNullException ("str");
1824 return InternalIsInterned (str);
1827 public static string Join (string separator, string [] value)
1830 throw new ArgumentNullException ("value");
1832 return Join (separator, value, 0, value.Length);
1835 public static string Join (string separator, string[] value, int startIndex, int count)
1838 throw new ArgumentNullException ("value");
1840 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1842 throw new ArgumentOutOfRangeException ("count", "< 0");
1843 // re-ordered to avoid possible integer overflow
1844 if (startIndex > value.Length - count)
1845 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1847 if (startIndex == value.Length)
1848 return String.Empty;
1849 if (separator == null)
1850 separator = String.Empty;
1852 return InternalJoin (separator, value, startIndex, count);
1855 bool IConvertible.ToBoolean (IFormatProvider provider)
1857 return Convert.ToBoolean (this, provider);
1860 byte IConvertible.ToByte (IFormatProvider provider)
1862 return Convert.ToByte (this, provider);
1865 char IConvertible.ToChar (IFormatProvider provider)
1867 return Convert.ToChar (this, provider);
1870 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1872 return Convert.ToDateTime (this, provider);
1875 decimal IConvertible.ToDecimal (IFormatProvider provider)
1877 return Convert.ToDecimal (this, provider);
1880 double IConvertible.ToDouble (IFormatProvider provider)
1882 return Convert.ToDouble (this, provider);
1885 short IConvertible.ToInt16 (IFormatProvider provider)
1887 return Convert.ToInt16 (this, provider);
1890 int IConvertible.ToInt32 (IFormatProvider provider)
1892 return Convert.ToInt32 (this, provider);
1895 long IConvertible.ToInt64 (IFormatProvider provider)
1897 return Convert.ToInt64 (this, provider);
1900 sbyte IConvertible.ToSByte (IFormatProvider provider)
1902 return Convert.ToSByte (this, provider);
1905 float IConvertible.ToSingle (IFormatProvider provider)
1907 return Convert.ToSingle (this, provider);
1910 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1912 return Convert.ToType (this, conversionType, provider);
1915 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1917 return Convert.ToUInt16 (this, provider);
1920 uint IConvertible.ToUInt32 (IFormatProvider provider)
1922 return Convert.ToUInt32 (this, provider);
1925 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1927 return Convert.ToUInt64 (this, provider);
1936 public CharEnumerator GetEnumerator ()
1938 return new CharEnumerator (this);
1942 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
1944 return GetEnumerator ();
1948 IEnumerator IEnumerable.GetEnumerator ()
1950 return new CharEnumerator (this);
1953 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1954 out bool left_align, out string format)
1956 // parses format specifier of form:
1962 // N = argument number (non-negative integer)
1964 n = ParseDecimal (str, ref ptr);
1966 throw new FormatException ("Input string was not in a correct format.");
1968 // M = width (non-negative integer)
1970 if (str[ptr] == ',') {
1971 // White space between ',' and number or sign.
1973 while (Char.IsWhiteSpace (str [ptr]))
1977 format = str.Substring (start, ptr - start);
1979 left_align = (str [ptr] == '-');
1983 width = ParseDecimal (str, ref ptr);
1985 throw new FormatException ("Input string was not in a correct format.");
1990 format = String.Empty;
1993 // F = argument format (string)
1995 if (str[ptr] == ':') {
1997 while (str[ptr] != '}')
2000 format += str.Substring (start, ptr - start);
2005 if (str[ptr ++] != '}')
2006 throw new FormatException ("Input string was not in a correct format.");
2008 catch (IndexOutOfRangeException) {
2009 throw new FormatException ("Input string was not in a correct format.");
2013 private static int ParseDecimal (string str, ref int ptr)
2019 if (c < '0' || '9' < c)
2022 n = n * 10 + c - '0';
2033 internal unsafe void InternalSetChar (int idx, char val)
2035 if ((uint) idx >= (uint) Length)
2036 throw new ArgumentOutOfRangeException ("idx");
2038 fixed (char * pStr = &start_char)
2044 internal unsafe void InternalSetLength (int newLength)
2046 if (newLength > length)
2047 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2049 // zero terminate, we can pass string objects directly via pinvoke
2050 // we also zero the rest of the string, since the new GC needs to be
2051 // able to handle the changing size (it will skip the 0 bytes).
2052 fixed (char * pStr = &start_char) {
2053 char *p = pStr + newLength;
2054 char *end = pStr + length;
2064 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2066 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2067 public unsafe override int GetHashCode ()
2069 fixed (char * c = this) {
2071 char * end = cc + length - 1;
2073 for (;cc < end; cc += 2) {
2074 h = (h << 5) - h + *cc;
2075 h = (h << 5) - h + cc [1];
2079 h = (h << 5) - h + *cc;
2084 internal unsafe int GetCaseInsensitiveHashCode ()
2086 TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
2087 fixed (char * c = this) {
2089 char * end = cc + length - 1;
2091 for (;cc < end; cc += 2) {
2092 h = (h << 5) - h + ti.ToUpper (*cc);
2093 h = (h << 5) - h + ti.ToUpper (cc [1]);
2097 h = (h << 5) - h + ti.ToUpper (*cc);
2102 // Certain constructors are redirected to CreateString methods with
2103 // matching argument list. The this pointer should not be used.
2105 private unsafe String CreateString (sbyte* value)
2108 return String.Empty;
2110 byte* bytes = (byte*) value;
2114 while (bytes++ [0] != 0)
2116 } catch (NullReferenceException) {
2117 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
2119 } catch (AccessViolationException) {
2120 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
2124 return CreateString (value, 0, length, null);
2127 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2129 return CreateString (value, startIndex, length, null);
2132 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2135 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2137 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2138 if (value + startIndex < value)
2139 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2141 bool isDefaultEncoding;
2143 if (isDefaultEncoding = (enc == null)) {
2146 throw new ArgumentNullException ("value");
2149 if (value == null || length == 0)
2151 return String.Empty;
2153 enc = Encoding.Default;
2156 byte [] bytes = new byte [length];
2159 fixed (byte* bytePtr = bytes)
2161 memcpy (bytePtr, (byte*) (value + startIndex), length);
2162 } catch (NullReferenceException) {
2164 if (!isDefaultEncoding)
2168 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2170 } catch (AccessViolationException) {
2171 if (!isDefaultEncoding)
2174 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2178 // GetString () is called even when length == 0
2179 return enc.GetString (bytes);
2182 /* helpers used by the runtime as well as above or eslewhere in corlib */
2183 internal static unsafe void memset (byte *dest, int val, int len)
2194 val = val | (val << 8);
2195 val = val | (val << 16);
2198 int rest = (int)dest & 3;
2206 } while (rest != 0);
2209 ((int*)dest) [0] = val;
2210 ((int*)dest) [1] = val;
2211 ((int*)dest) [2] = val;
2212 ((int*)dest) [3] = val;
2217 ((int*)dest) [0] = val;
2229 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2230 /*while (size >= 32) {
2231 // using long is better than int and slower than double
2232 // FIXME: enable this only on correct alignment or on platforms
2233 // that can tolerate unaligned reads/writes of doubles
2234 ((double*)dest) [0] = ((double*)src) [0];
2235 ((double*)dest) [1] = ((double*)src) [1];
2236 ((double*)dest) [2] = ((double*)src) [2];
2237 ((double*)dest) [3] = ((double*)src) [3];
2242 while (size >= 16) {
2243 ((int*)dest) [0] = ((int*)src) [0];
2244 ((int*)dest) [1] = ((int*)src) [1];
2245 ((int*)dest) [2] = ((int*)src) [2];
2246 ((int*)dest) [3] = ((int*)src) [3];
2252 ((int*)dest) [0] = ((int*)src) [0];
2258 ((byte*)dest) [0] = ((byte*)src) [0];
2264 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2266 ((short*)dest) [0] = ((short*)src) [0];
2267 ((short*)dest) [1] = ((short*)src) [1];
2268 ((short*)dest) [2] = ((short*)src) [2];
2269 ((short*)dest) [3] = ((short*)src) [3];
2275 ((short*)dest) [0] = ((short*)src) [0];
2281 ((byte*)dest) [0] = ((byte*)src) [0];
2283 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2285 ((byte*)dest) [0] = ((byte*)src) [0];
2286 ((byte*)dest) [1] = ((byte*)src) [1];
2287 ((byte*)dest) [2] = ((byte*)src) [2];
2288 ((byte*)dest) [3] = ((byte*)src) [3];
2289 ((byte*)dest) [4] = ((byte*)src) [4];
2290 ((byte*)dest) [5] = ((byte*)src) [5];
2291 ((byte*)dest) [6] = ((byte*)src) [6];
2292 ((byte*)dest) [7] = ((byte*)src) [7];
2298 ((byte*)dest) [0] = ((byte*)src) [0];
2299 ((byte*)dest) [1] = ((byte*)src) [1];
2305 ((byte*)dest) [0] = ((byte*)src) [0];
2308 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2309 // FIXME: if pointers are not aligned, try to align them
2310 // so a faster routine can be used. Handle the case where
2311 // the pointers can't be reduced to have the same alignment
2312 // (just ignore the issue on x86?)
2313 if ((((int)dest | (int)src) & 3) != 0) {
2314 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2320 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2321 ((short*)dest) [0] = ((short*)src) [0];
2326 if ((((int)dest | (int)src) & 1) != 0) {
2327 memcpy1 (dest, src, size);
2330 if ((((int)dest | (int)src) & 2) != 0) {
2331 memcpy2 (dest, src, size);
2335 memcpy4 (dest, src, size);
2338 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2339 unsafe public extern String (char *value);
2341 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2342 unsafe public extern String (char *value, int startIndex, int length);
2344 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2345 unsafe public extern String (sbyte *value);
2347 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2348 unsafe public extern String (sbyte *value, int startIndex, int length);
2350 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2351 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2353 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2354 public extern String (char [] val, int startIndex, int length);
2356 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2357 public extern String (char [] val);
2359 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2360 public extern String (char c, int count);
2362 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2363 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2365 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
2366 //private extern String InternalReplace (char oldChar, char newChar, int startIndex);
2368 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2369 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2371 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2372 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2374 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2375 private extern String[] InternalSplit (char[] separator, int count);
2377 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2378 private extern String InternalTrim (char[] chars, int typ);
2380 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
2381 //private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
2383 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2384 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2386 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2387 private extern String InternalPad (int width, char chr, bool right);
2389 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2390 internal extern static String InternalAllocateStr (int length);
2392 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2393 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2395 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2396 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2398 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2399 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2401 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2402 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2404 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2405 private extern static string InternalIntern (string str);
2407 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2408 private extern static string InternalIsInterned (string str);