Remove some string.Empty references
[mono-project.git] / mcs / class / corlib / System / String.cs
blob5844e98775f60ed1f3be5fc5a32b145d2c44c18d
1 //
2 // System.String.cs
3 //
4 // Authors:
5 // Patrik Torstensson
6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
40 // Ensure that argument checks on methods don't overflow
43 using System.Text;
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
48 using System.Collections.Generic;
49 using System.Runtime.ConstrainedExecution;
50 using System.Runtime.InteropServices;
51 using Mono.Globalization.Unicode;
54 namespace System
56 [Serializable]
57 [ComVisible (true)]
58 [StructLayout (LayoutKind.Sequential)]
59 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
61 [NonSerialized] private int length;
62 [NonSerialized] private char start_char;
64 public static readonly String Empty = "";
66 internal static readonly int LOS_limit = GetLOSLimit ();
68 public static unsafe bool Equals (string a, string b)
70 if ((a as object) == (b as object))
71 return true;
73 if (a == null || b == null)
74 return false;
76 int len = a.length;
78 if (len != b.length)
79 return false;
81 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
82 char* s1_ptr = s1;
83 char* s2_ptr = s2;
85 while (len >= 8) {
86 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
87 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
88 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
89 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
90 return false;
92 s1_ptr += 8;
93 s2_ptr += 8;
94 len -= 8;
97 if (len >= 4) {
98 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
99 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
100 return false;
102 s1_ptr += 4;
103 s2_ptr += 4;
104 len -= 4;
107 if (len > 1) {
108 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
109 return false;
111 s1_ptr += 2;
112 s2_ptr += 2;
113 len -= 2;
116 return len == 0 || *s1_ptr == *s2_ptr;
120 public static bool operator == (String a, String b)
122 return Equals (a, b);
125 public static bool operator != (String a, String b)
127 return !Equals (a, b);
130 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
131 public override bool Equals (Object obj)
133 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
137 public bool Equals (String value)
139 return Equals (this, value);
142 [IndexerName ("Chars")]
143 public unsafe char this [int index] {
144 get {
145 if (index < 0 || index >= length)
146 throw new IndexOutOfRangeException ();
147 fixed (char* c = &start_char)
148 return c[index];
152 public Object Clone ()
154 return this;
157 public TypeCode GetTypeCode ()
159 return TypeCode.String;
162 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
164 if (destination == null)
165 throw new ArgumentNullException ("destination");
166 if (sourceIndex < 0)
167 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
168 if (destinationIndex < 0)
169 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
170 if (count < 0)
171 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
172 if (sourceIndex > Length - count)
173 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
174 if (destinationIndex > destination.Length - count)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
177 fixed (char* dest = destination, src = this)
178 CharCopy (dest + destinationIndex, src + sourceIndex, count);
181 public char[] ToCharArray ()
183 return ToCharArray (0, length);
186 public unsafe char[] ToCharArray (int startIndex, int length)
188 if (startIndex < 0)
189 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
190 if (length < 0)
191 throw new ArgumentOutOfRangeException ("length", "< 0");
192 if (startIndex > this.length - length)
193 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
195 char[] tmp = new char [length];
196 fixed (char* dest = tmp, src = this)
197 CharCopy (dest, src + startIndex, length);
198 return tmp;
201 public String [] Split (params char [] separator)
203 return Split (separator, int.MaxValue, 0);
206 public String[] Split (char[] separator, int count)
208 return Split (separator, count, 0);
211 [ComVisible (false)]
212 public String[] Split (char[] separator, StringSplitOptions options)
214 return Split (separator, Int32.MaxValue, options);
217 [ComVisible (false)]
218 public String[] Split (char[] separator, int count, StringSplitOptions options)
220 if (count < 0)
221 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
222 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
223 throw new ArgumentException ("Illegal enum value: " + options + ".");
225 if (count <= 1) {
226 return count == 0 ?
227 new String[0] :
228 new String[1] { this };
231 return SplitByCharacters (separator, count, options != 0);
234 [ComVisible (false)]
235 public String[] Split (string[] separator, StringSplitOptions options)
237 return Split (separator, Int32.MaxValue, options);
240 [ComVisible (false)]
241 public String[] Split (string[] separator, int count, StringSplitOptions options)
243 if (count < 0)
244 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
245 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
246 throw new ArgumentException ("Illegal enum value: " + options + ".");
248 if (count <= 1) {
249 return count == 0 ?
250 new String[0] :
251 new String[1] { this };
254 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
256 if (separator == null || separator.Length == 0)
257 return SplitByCharacters (null, count, removeEmpty);
259 if (Length == 0 && removeEmpty)
260 return new String [0];
262 List<String> arr = new List<String> ();
264 int pos = 0;
265 int matchCount = 0;
266 while (pos < this.Length) {
267 int matchIndex = -1;
268 int matchPos = Int32.MaxValue;
270 // Find the first position where any of the separators matches
271 for (int i = 0; i < separator.Length; ++i) {
272 string sep = separator [i];
273 if (sep == null || sep.Length == 0)
274 continue;
276 int match = IndexOfOrdinalUnchecked (sep, pos, Length);
277 if (match > -1 && match < matchPos) {
278 matchIndex = i;
279 matchPos = match;
283 if (matchIndex == -1)
284 break;
286 if (!(matchPos == pos && removeEmpty)) {
287 if (arr.Count == count - 1)
288 break;
289 arr.Add (this.Substring (pos, matchPos - pos));
292 pos = matchPos + separator [matchIndex].Length;
294 matchCount ++;
297 if (matchCount == 0)
298 return new String [] { this };
300 // string contained only separators
301 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
302 return new String [0];
304 if (!(removeEmpty && pos == this.Length))
305 arr.Add (this.Substring (pos));
307 return arr.ToArray ();
310 // .NET 2.0 compatibility only
311 #if !NET_4_0 && !MOONLIGHT && !MOBILE
312 static readonly char[] WhiteChars = {
313 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
314 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
315 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
316 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
317 (char) 0x3000, (char) 0xFEFF
319 #endif
321 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
323 #if !NET_4_0 && !MOONLIGHT && !MOBILE
324 if (sep == null || sep.Length == 0)
325 sep = WhiteChars;
326 #endif
328 int[] split_points = null;
329 int total_points = 0;
330 --count;
332 if (sep == null || sep.Length == 0) {
333 fixed (char* src = this) {
334 char* src_ptr = src;
335 int len = Length;
337 do {
338 if (char.IsWhiteSpace (*src_ptr++)) {
339 if (split_points == null) {
340 split_points = new int[8];
341 } else if (split_points.Length == total_points) {
342 Array.Resize (ref split_points, split_points.Length * 2);
345 split_points[total_points++] = Length - len;
346 if (total_points == count && !removeEmpty)
347 break;
349 } while (len-- != 0);
351 } else {
352 fixed (char* src = this) {
353 fixed (char* sep_src = sep) {
354 char* src_ptr = src;
355 char* sep_ptr_end = sep_src + sep.Length;
356 int len = Length;
357 do {
358 char* sep_ptr = sep_src;
359 do {
360 if (*sep_ptr++ == *src_ptr) {
361 if (split_points == null) {
362 split_points = new int[8];
363 } else if (split_points.Length == total_points) {
364 Array.Resize (ref split_points, split_points.Length * 2);
367 split_points[total_points++] = Length - len;
368 if (total_points == count && !removeEmpty)
369 len = 0;
371 break;
373 } while (sep_ptr != sep_ptr_end);
375 ++src_ptr;
376 } while (len-- != 0);
381 if (total_points == 0)
382 return new string[] { this };
384 var res = new string[Math.Min (total_points, count) + 1];
385 int prev_index = 0;
386 int i = 0;
387 if (!removeEmpty) {
388 for (; i < total_points; ++i) {
389 var start = split_points[i];
390 res[i] = SubstringUnchecked (prev_index, start - prev_index);
391 prev_index = start + 1;
394 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
395 } else {
396 int used = 0;
397 int length;
398 for (; i < total_points; ++i) {
399 var start = split_points[i];
400 length = start - prev_index;
401 if (length != 0) {
402 if (used == count)
403 break;
405 res[used++] = SubstringUnchecked (prev_index, length);
408 prev_index = start + 1;
411 length = Length - prev_index;
412 if (length != 0)
413 res[used++] = SubstringUnchecked (prev_index, length);
415 if (used != res.Length)
416 Array.Resize (ref res, used);
419 return res;
422 public String Substring (int startIndex)
424 if (startIndex == 0)
425 return this;
426 if (startIndex < 0 || startIndex > this.length)
427 throw new ArgumentOutOfRangeException ("startIndex");
429 return SubstringUnchecked (startIndex, this.length - startIndex);
432 public String Substring (int startIndex, int length)
434 if (length < 0)
435 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
436 if (startIndex < 0)
437 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
438 if (startIndex > this.length)
439 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
440 if (startIndex > this.length - length)
441 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
442 if (startIndex == 0 && length == this.length)
443 return this;
445 return SubstringUnchecked (startIndex, length);
448 // This method is used by StringBuilder.ToString() and is expected to
449 // always create a new string object (or return String.Empty).
450 internal unsafe String SubstringUnchecked (int startIndex, int length)
452 if (length == 0)
453 return Empty;
455 string tmp = InternalAllocateStr (length);
456 fixed (char* dest = tmp, src = this) {
457 CharCopy (dest, src + startIndex, length);
459 return tmp;
462 public String Trim ()
464 if (length == 0)
465 return Empty;
466 int start = FindNotWhiteSpace (0, length, 1);
468 if (start == length)
469 return Empty;
471 int end = FindNotWhiteSpace (length - 1, start, -1);
473 int newLength = end - start + 1;
474 if (newLength == length)
475 return this;
477 return SubstringUnchecked (start, newLength);
480 public String Trim (params char[] trimChars)
482 if (trimChars == null || trimChars.Length == 0)
483 return Trim ();
485 if (length == 0)
486 return Empty;
487 int start = FindNotInTable (0, length, 1, trimChars);
489 if (start == length)
490 return Empty;
492 int end = FindNotInTable (length - 1, start, -1, trimChars);
494 int newLength = end - start + 1;
495 if (newLength == length)
496 return this;
498 return SubstringUnchecked (start, newLength);
501 public String TrimStart (params char[] trimChars)
503 if (length == 0)
504 return Empty;
505 int start;
506 if (trimChars == null || trimChars.Length == 0)
507 start = FindNotWhiteSpace (0, length, 1);
508 else
509 start = FindNotInTable (0, length, 1, trimChars);
511 if (start == 0)
512 return this;
514 return SubstringUnchecked (start, length - start);
517 public String TrimEnd (params char[] trimChars)
519 if (length == 0)
520 return Empty;
521 int end;
522 if (trimChars == null || trimChars.Length == 0)
523 end = FindNotWhiteSpace (length - 1, -1, -1);
524 else
525 end = FindNotInTable (length - 1, -1, -1, trimChars);
527 end++;
528 if (end == length)
529 return this;
531 return SubstringUnchecked (0, end);
534 unsafe int FindNotWhiteSpace (int pos, int target, int change)
536 #if NET_4_0 || NET_2_1
537 fixed (char* src = this) {
538 while (pos != target) {
539 if (!char.IsWhiteSpace (src[pos]))
540 return pos;
542 pos += change;
545 #else
546 while (pos != target) {
547 char c = this[pos];
548 if (c < 0x85) {
549 if (c != 0x20) {
550 if (c < 0x9 || c > 0xD)
551 return pos;
554 else {
555 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
556 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
557 if (c < 0x2000 || c > 0x200B)
558 return pos;
561 pos += change;
563 #endif
564 return pos;
567 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
569 fixed (char* tablePtr = table, thisPtr = this) {
570 while (pos != target) {
571 char c = thisPtr[pos];
572 int x = 0;
573 while (x < table.Length) {
574 if (c == tablePtr[x])
575 break;
576 x++;
578 if (x == table.Length)
579 return pos;
580 pos += change;
583 return pos;
586 public static int Compare (String strA, String strB)
588 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
591 public static int Compare (String strA, String strB, bool ignoreCase)
593 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
596 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
598 if (culture == null)
599 throw new ArgumentNullException ("culture");
601 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
604 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
606 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
609 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
611 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
614 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
616 if (culture == null)
617 throw new ArgumentNullException ("culture");
619 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
620 throw new ArgumentOutOfRangeException ();
622 if (length == 0)
623 return 0;
625 if (strA == null) {
626 if (strB == null) {
627 return 0;
628 } else {
629 return -1;
632 else if (strB == null) {
633 return 1;
636 CompareOptions compopts;
638 if (ignoreCase)
639 compopts = CompareOptions.IgnoreCase;
640 else
641 compopts = CompareOptions.None;
643 // Need to cap the requested length to the
644 // length of the string, because
645 // CompareInfo.Compare will insist that length
646 // <= (string.Length - offset)
648 int len1 = length;
649 int len2 = length;
651 if (length > (strA.Length - indexA)) {
652 len1 = strA.Length - indexA;
655 if (length > (strB.Length - indexB)) {
656 len2 = strB.Length - indexB;
659 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
660 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
663 public static int Compare (string strA, string strB, StringComparison comparisonType)
665 switch (comparisonType) {
666 case StringComparison.CurrentCulture:
667 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
668 case StringComparison.CurrentCultureIgnoreCase:
669 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
670 case StringComparison.InvariantCulture:
671 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
672 case StringComparison.InvariantCultureIgnoreCase:
673 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
674 case StringComparison.Ordinal:
675 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
676 case StringComparison.OrdinalIgnoreCase:
677 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
678 default:
679 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
680 throw new ArgumentException (msg, "comparisonType");
684 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
686 switch (comparisonType) {
687 case StringComparison.CurrentCulture:
688 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
689 case StringComparison.CurrentCultureIgnoreCase:
690 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
691 case StringComparison.InvariantCulture:
692 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
693 case StringComparison.InvariantCultureIgnoreCase:
694 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
695 case StringComparison.Ordinal:
696 return CompareOrdinal (strA, indexA, strB, indexB, length);
697 case StringComparison.OrdinalIgnoreCase:
698 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
699 default:
700 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
701 throw new ArgumentException (msg, "comparisonType");
705 public static bool Equals (string a, string b, StringComparison comparisonType)
707 return String.Compare (a, b, comparisonType) == 0;
710 public bool Equals (string value, StringComparison comparisonType)
712 return String.Compare (value, this, comparisonType) == 0;
715 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
717 if (culture == null)
718 throw new ArgumentNullException ("culture");
720 return culture.CompareInfo.Compare (strA, strB, options);
723 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
725 if (culture == null)
726 throw new ArgumentNullException ("culture");
728 int len1 = length;
729 int len2 = length;
731 if (length > (strA.Length - indexA))
732 len1 = strA.Length - indexA;
734 if (length > (strB.Length - indexB))
735 len2 = strB.Length - indexB;
737 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
740 public int CompareTo (Object value)
742 if (value == null)
743 return 1;
745 if (!(value is String))
746 throw new ArgumentException ();
748 return String.Compare (this, (String) value);
751 public int CompareTo (String strB)
753 if (strB == null)
754 return 1;
756 return Compare (this, strB);
759 public static int CompareOrdinal (String strA, String strB)
761 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
764 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
766 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
767 throw new ArgumentOutOfRangeException ();
769 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
772 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
774 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
775 throw new ArgumentOutOfRangeException ();
777 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
780 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
782 if (strA == null) {
783 if (strB == null)
784 return 0;
785 else
786 return -1;
787 } else if (strB == null) {
788 return 1;
790 int lengthA = Math.Min (lenA, strA.Length - indexA);
791 int lengthB = Math.Min (lenB, strB.Length - indexB);
793 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
794 return 0;
796 fixed (char* aptr = strA, bptr = strB) {
797 char* ap = aptr + indexA;
798 char* end = ap + Math.Min (lengthA, lengthB);
799 char* bp = bptr + indexB;
800 while (ap < end) {
801 if (*ap != *bp)
802 return *ap - *bp;
803 ap++;
804 bp++;
806 return lengthA - lengthB;
810 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
812 // Same as above, but checks versus uppercase characters
813 if (strA == null) {
814 if (strB == null)
815 return 0;
816 else
817 return -1;
818 } else if (strB == null) {
819 return 1;
821 int lengthA = Math.Min (lenA, strA.Length - indexA);
822 int lengthB = Math.Min (lenB, strB.Length - indexB);
824 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
825 return 0;
827 fixed (char* aptr = strA, bptr = strB) {
828 char* ap = aptr + indexA;
829 char* end = ap + Math.Min (lengthA, lengthB);
830 char* bp = bptr + indexB;
831 while (ap < end) {
832 if (*ap != *bp) {
833 char c1 = Char.ToUpperInvariant (*ap);
834 char c2 = Char.ToUpperInvariant (*bp);
835 if (c1 != c2)
836 return c1 - c2;
838 ap++;
839 bp++;
841 return lengthA - lengthB;
845 public bool EndsWith (String value)
847 if (value == null)
848 throw new ArgumentNullException ("value");
850 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
853 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
855 if (value == null)
856 throw new ArgumentNullException ("value");
857 if (culture == null)
858 culture = CultureInfo.CurrentCulture;
860 return culture.CompareInfo.IsSuffix (this, value,
861 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
864 // Following methods are culture-insensitive
865 public int IndexOfAny (char [] anyOf)
867 if (anyOf == null)
868 throw new ArgumentNullException ();
869 if (this.length == 0)
870 return -1;
872 return IndexOfAnyUnchecked (anyOf, 0, this.length);
875 public int IndexOfAny (char [] anyOf, int startIndex)
877 if (anyOf == null)
878 throw new ArgumentNullException ();
879 if (startIndex < 0 || startIndex > this.length)
880 throw new ArgumentOutOfRangeException ();
882 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
885 public int IndexOfAny (char [] anyOf, int startIndex, int count)
887 if (anyOf == null)
888 throw new ArgumentNullException ();
889 if (startIndex < 0 || startIndex > this.length)
890 throw new ArgumentOutOfRangeException ();
891 if (count < 0 || startIndex > this.length - count)
892 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
894 return IndexOfAnyUnchecked (anyOf, startIndex, count);
897 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
899 if (anyOf.Length == 0)
900 return -1;
902 if (anyOf.Length == 1)
903 return IndexOfUnchecked (anyOf[0], startIndex, count);
905 fixed (char* any = anyOf) {
906 int highest = *any;
907 int lowest = *any;
909 char* end_any_ptr = any + anyOf.Length;
910 char* any_ptr = any;
911 while (++any_ptr != end_any_ptr) {
912 if (*any_ptr > highest) {
913 highest = *any_ptr;
914 continue;
917 if (*any_ptr < lowest)
918 lowest = *any_ptr;
921 fixed (char* start = &start_char) {
922 char* ptr = start + startIndex;
923 char* end_ptr = ptr + count;
925 while (ptr != end_ptr) {
926 if (*ptr > highest || *ptr < lowest) {
927 ptr++;
928 continue;
931 if (*ptr == *any)
932 return (int)(ptr - start);
934 any_ptr = any;
935 while (++any_ptr != end_any_ptr) {
936 if (*ptr == *any_ptr)
937 return (int)(ptr - start);
940 ptr++;
944 return -1;
948 public int IndexOf (string value, StringComparison comparisonType)
950 return IndexOf (value, 0, this.Length, comparisonType);
953 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
955 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
958 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
960 switch (comparisonType) {
961 case StringComparison.CurrentCulture:
962 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
963 case StringComparison.CurrentCultureIgnoreCase:
964 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
965 case StringComparison.InvariantCulture:
966 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
967 case StringComparison.InvariantCultureIgnoreCase:
968 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
969 case StringComparison.Ordinal:
970 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
971 case StringComparison.OrdinalIgnoreCase:
972 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
973 default:
974 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
975 throw new ArgumentException (msg, "comparisonType");
979 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
981 if (value == null)
982 throw new ArgumentNullException ("value");
983 if (startIndex < 0)
984 throw new ArgumentOutOfRangeException ("startIndex");
985 if (count < 0 || (this.length - startIndex) < count)
986 throw new ArgumentOutOfRangeException ("count");
988 if (options == CompareOptions.Ordinal)
989 return IndexOfOrdinalUnchecked (value, startIndex, count);
990 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
993 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
995 int valueLen = value.Length;
996 if (count < valueLen)
997 return -1;
999 if (valueLen <= 1) {
1000 if (valueLen == 1)
1001 return IndexOfUnchecked (value[0], startIndex, count);
1002 return startIndex;
1005 fixed (char* thisptr = this, valueptr = value) {
1006 char* ap = thisptr + startIndex;
1007 char* thisEnd = ap + count - valueLen + 1;
1008 while (ap != thisEnd) {
1009 if (*ap == *valueptr) {
1010 for (int i = 1; i < valueLen; i++) {
1011 if (ap[i] != valueptr[i])
1012 goto NextVal;
1014 return (int)(ap - thisptr);
1016 NextVal:
1017 ap++;
1020 return -1;
1023 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1025 int valueLen = value.Length;
1026 if (count < valueLen)
1027 return -1;
1029 if (valueLen == 0)
1030 return startIndex;
1032 fixed (char* thisptr = this, valueptr = value) {
1033 char* ap = thisptr + startIndex;
1034 char* thisEnd = ap + count - valueLen + 1;
1035 while (ap != thisEnd) {
1036 for (int i = 0; i < valueLen; i++) {
1037 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1038 goto NextVal;
1040 return (int)(ap - thisptr);
1041 NextVal:
1042 ap++;
1045 return -1;
1048 public int LastIndexOf (string value, StringComparison comparisonType)
1050 if (this.Length == 0)
1051 return value.Length == 0 ? 0 : -1;
1052 else
1053 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1056 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1058 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1061 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1063 switch (comparisonType) {
1064 case StringComparison.CurrentCulture:
1065 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1066 case StringComparison.CurrentCultureIgnoreCase:
1067 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1068 case StringComparison.InvariantCulture:
1069 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1070 case StringComparison.InvariantCultureIgnoreCase:
1071 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1072 case StringComparison.Ordinal:
1073 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1074 case StringComparison.OrdinalIgnoreCase:
1075 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1076 default:
1077 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1078 throw new ArgumentException (msg, "comparisonType");
1082 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1084 if (value == null)
1085 throw new ArgumentNullException ("value");
1086 if (this.Length == 0)
1087 return value.Length == 0 ? 0 : -1;
1088 if (value.Length == 0)
1089 return Math.Min (this.Length - 1, startIndex);
1090 if (startIndex < 0 || startIndex > length)
1091 throw new ArgumentOutOfRangeException ("startIndex");
1092 if (count < 0 || (startIndex < count - 1))
1093 throw new ArgumentOutOfRangeException ("count");
1095 if (options == CompareOptions.Ordinal)
1096 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1097 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1100 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1102 int valueLen = value.Length;
1103 if (count < valueLen)
1104 return -1;
1106 if (valueLen <= 1) {
1107 if (valueLen == 1)
1108 return LastIndexOfUnchecked (value[0], startIndex, count);
1109 return startIndex;
1112 fixed (char* thisptr = this, valueptr = value) {
1113 char* ap = thisptr + startIndex - valueLen + 1;
1114 char* thisEnd = ap - count + valueLen - 1;
1115 while (ap != thisEnd) {
1116 if (*ap == *valueptr) {
1117 for (int i = 1; i < valueLen; i++) {
1118 if (ap[i] != valueptr[i])
1119 goto NextVal;
1121 return (int)(ap - thisptr);
1123 NextVal:
1124 ap--;
1127 return -1;
1130 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1132 int valueLen = value.Length;
1133 if (count < valueLen)
1134 return -1;
1136 if (valueLen == 0)
1137 return startIndex;
1139 fixed (char* thisptr = this, valueptr = value) {
1140 char* ap = thisptr + startIndex - valueLen + 1;
1141 char* thisEnd = ap - count + valueLen - 1;
1142 while (ap != thisEnd) {
1143 for (int i = 0; i < valueLen; i++) {
1144 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1145 goto NextVal;
1147 return (int)(ap - thisptr);
1148 NextVal:
1149 ap--;
1152 return -1;
1155 // Following methods are culture-insensitive
1156 public int IndexOf (char value)
1158 if (this.length == 0)
1159 return -1;
1161 return IndexOfUnchecked (value, 0, this.length);
1164 public int IndexOf (char value, int startIndex)
1166 if (startIndex < 0)
1167 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1168 if (startIndex > this.length)
1169 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1171 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1172 return -1;
1174 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1177 public int IndexOf (char value, int startIndex, int count)
1179 if (startIndex < 0 || startIndex > this.length)
1180 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1181 if (count < 0)
1182 throw new ArgumentOutOfRangeException ("count", "< 0");
1183 if (startIndex > this.length - count)
1184 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1186 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1187 return -1;
1189 return IndexOfUnchecked (value, startIndex, count);
1192 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1194 // It helps JIT compiler to optimize comparison
1195 int value_32 = (int)value;
1197 fixed (char* start = &start_char) {
1198 char* ptr = start + startIndex;
1199 char* end_ptr = ptr + (count >> 3 << 3);
1201 while (ptr != end_ptr) {
1202 if (*ptr == value_32)
1203 return (int)(ptr - start);
1204 if (ptr[1] == value_32)
1205 return (int)(ptr - start + 1);
1206 if (ptr[2] == value_32)
1207 return (int)(ptr - start + 2);
1208 if (ptr[3] == value_32)
1209 return (int)(ptr - start + 3);
1210 if (ptr[4] == value_32)
1211 return (int)(ptr - start + 4);
1212 if (ptr[5] == value_32)
1213 return (int)(ptr - start + 5);
1214 if (ptr[6] == value_32)
1215 return (int)(ptr - start + 6);
1216 if (ptr[7] == value_32)
1217 return (int)(ptr - start + 7);
1219 ptr += 8;
1222 end_ptr += count & 0x07;
1223 while (ptr != end_ptr) {
1224 if (*ptr == value_32)
1225 return (int)(ptr - start);
1227 ptr++;
1229 return -1;
1233 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1235 if (length == 0)
1236 return -1;
1237 int end = startIndex + count;
1238 char c = Char.ToUpperInvariant (value);
1239 fixed (char* s = &start_char) {
1240 for (int i = startIndex; i < end; i++)
1241 if (Char.ToUpperInvariant (s [i]) == c)
1242 return i;
1244 return -1;
1247 // Following methods are culture-sensitive
1248 public int IndexOf (String value)
1250 if (value == null)
1251 throw new ArgumentNullException ("value");
1252 if (value.length == 0)
1253 return 0;
1254 if (this.length == 0)
1255 return -1;
1256 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.Ordinal);
1259 public int IndexOf (String value, int startIndex)
1261 return IndexOf (value, startIndex, this.length - startIndex);
1264 public int IndexOf (String value, int startIndex, int count)
1266 if (value == null)
1267 throw new ArgumentNullException ("value");
1268 if (startIndex < 0 || startIndex > this.length)
1269 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1270 if (count < 0 || startIndex > this.length - count)
1271 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1273 if (value.length == 0)
1274 return startIndex;
1276 if (startIndex == 0 && this.length == 0)
1277 return -1;
1279 if (count == 0)
1280 return -1;
1282 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1285 // Following methods are culture-insensitive
1286 public int LastIndexOfAny (char [] anyOf)
1288 if (anyOf == null)
1289 throw new ArgumentNullException ();
1290 if (this.length == 0)
1291 return -1;
1293 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1296 public int LastIndexOfAny (char [] anyOf, int startIndex)
1298 if (anyOf == null)
1299 throw new ArgumentNullException ();
1300 if (this.length == 0)
1301 return -1;
1303 if (startIndex < 0 || startIndex >= this.length)
1304 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1306 if (this.length == 0)
1307 return -1;
1309 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1312 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1314 if (anyOf == null)
1315 throw new ArgumentNullException ();
1316 if (this.length == 0)
1317 return -1;
1319 if ((startIndex < 0) || (startIndex >= this.Length))
1320 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1321 if ((count < 0) || (count > this.Length))
1322 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1323 if (startIndex - count + 1 < 0)
1324 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1326 if (this.length == 0)
1327 return -1;
1329 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1332 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1334 if (anyOf.Length == 1)
1335 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1337 fixed (char* start = this, testStart = anyOf) {
1338 char* ptr = start + startIndex;
1339 char* ptrEnd = ptr - count;
1340 char* test;
1341 char* testEnd = testStart + anyOf.Length;
1343 while (ptr != ptrEnd) {
1344 test = testStart;
1345 while (test != testEnd) {
1346 if (*test == *ptr)
1347 return (int)(ptr - start);
1348 test++;
1350 ptr--;
1352 return -1;
1356 // Following methods are culture-insensitive
1357 public int LastIndexOf (char value)
1359 if (this.length == 0)
1360 return -1;
1362 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1365 public int LastIndexOf (char value, int startIndex)
1367 return LastIndexOf (value, startIndex, startIndex + 1);
1370 public int LastIndexOf (char value, int startIndex, int count)
1372 if (this.length == 0)
1373 return -1;
1375 // >= for char (> for string)
1376 if ((startIndex < 0) || (startIndex >= this.Length))
1377 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1378 if ((count < 0) || (count > this.Length))
1379 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1380 if (startIndex - count + 1 < 0)
1381 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1383 return LastIndexOfUnchecked (value, startIndex, count);
1386 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1388 // It helps JIT compiler to optimize comparison
1389 int value_32 = (int)value;
1391 fixed (char* start = &start_char) {
1392 char* ptr = start + startIndex;
1393 char* end_ptr = ptr - (count >> 3 << 3);
1395 while (ptr != end_ptr) {
1396 if (*ptr == value_32)
1397 return (int)(ptr - start);
1398 if (ptr[-1] == value_32)
1399 return (int)(ptr - start) - 1;
1400 if (ptr[-2] == value_32)
1401 return (int)(ptr - start) - 2;
1402 if (ptr[-3] == value_32)
1403 return (int)(ptr - start) - 3;
1404 if (ptr[-4] == value_32)
1405 return (int)(ptr - start) - 4;
1406 if (ptr[-5] == value_32)
1407 return (int)(ptr - start) - 5;
1408 if (ptr[-6] == value_32)
1409 return (int)(ptr - start) - 6;
1410 if (ptr[-7] == value_32)
1411 return (int)(ptr - start) - 7;
1413 ptr -= 8;
1416 end_ptr -= count & 0x07;
1417 while (ptr != end_ptr) {
1418 if (*ptr == value_32)
1419 return (int)(ptr - start);
1421 ptr--;
1423 return -1;
1427 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1429 if (length == 0)
1430 return -1;
1431 int end = startIndex - count;
1432 char c = Char.ToUpperInvariant (value);
1433 fixed (char* s = &start_char) {
1434 for (int i = startIndex; i > end; i--)
1435 if (Char.ToUpperInvariant (s [i]) == c)
1436 return i;
1438 return -1;
1441 // Following methods are culture-sensitive
1442 public int LastIndexOf (String value)
1444 return LastIndexOf (value, this.length - 1, this.length);
1447 public int LastIndexOf (String value, int startIndex)
1449 int max = startIndex;
1450 if (max < this.Length)
1451 max++;
1452 return LastIndexOf (value, startIndex, max);
1455 public int LastIndexOf (String value, int startIndex, int count)
1457 if (value == null)
1458 throw new ArgumentNullException ("value");
1460 if (this.length == 0)
1461 return value.Length == 0 ? 0 : -1;
1462 // -1 > startIndex > for string (0 > startIndex >= for char)
1463 if ((startIndex < -1) || (startIndex > this.Length))
1464 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1465 if ((count < 0) || (count > this.Length))
1466 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1467 if (startIndex - count + 1 < 0)
1468 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1470 if (value.Length == 0)
1471 return Math.Min (this.Length - 1, startIndex);
1473 if (startIndex == 0 && this.length == 0)
1474 return -1;
1476 // This check is needed to match undocumented MS behaviour
1477 if (this.length == 0 && value.length > 0)
1478 return -1;
1480 if (count == 0)
1481 return -1;
1483 if (startIndex == this.Length)
1484 startIndex--;
1485 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1488 public bool Contains (String value)
1490 return IndexOf (value) != -1;
1493 public static bool IsNullOrEmpty (String value)
1495 return (value == null) || (value.Length == 0);
1498 #if !MOONLIGHT
1499 public string Normalize ()
1501 return Normalization.Normalize (this, 0);
1504 public string Normalize (NormalizationForm normalizationForm)
1506 switch (normalizationForm) {
1507 default:
1508 return Normalization.Normalize (this, 0);
1509 case NormalizationForm.FormD:
1510 return Normalization.Normalize (this, 1);
1511 case NormalizationForm.FormKC:
1512 return Normalization.Normalize (this, 2);
1513 case NormalizationForm.FormKD:
1514 return Normalization.Normalize (this, 3);
1518 public bool IsNormalized ()
1520 return Normalization.IsNormalized (this, 0);
1523 public bool IsNormalized (NormalizationForm normalizationForm)
1525 switch (normalizationForm) {
1526 default:
1527 return Normalization.IsNormalized (this, 0);
1528 case NormalizationForm.FormD:
1529 return Normalization.IsNormalized (this, 1);
1530 case NormalizationForm.FormKC:
1531 return Normalization.IsNormalized (this, 2);
1532 case NormalizationForm.FormKD:
1533 return Normalization.IsNormalized (this, 3);
1536 #endif
1538 public string Remove (int startIndex)
1540 if (startIndex < 0)
1541 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1542 if (startIndex >= this.length)
1543 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1545 return Remove (startIndex, this.length - startIndex);
1548 public String PadLeft (int totalWidth)
1550 return PadLeft (totalWidth, ' ');
1553 public unsafe String PadLeft (int totalWidth, char paddingChar)
1555 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1557 if (totalWidth < 0)
1558 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1560 if (totalWidth < this.length)
1561 return this;
1562 if (totalWidth == 0)
1563 return Empty;
1565 String tmp = InternalAllocateStr (totalWidth);
1567 fixed (char* dest = tmp, src = this) {
1568 char* padPos = dest;
1569 char* padTo = dest + (totalWidth - length);
1570 while (padPos != padTo)
1571 *padPos++ = paddingChar;
1573 CharCopy (padTo, src, length);
1575 return tmp;
1578 public String PadRight (int totalWidth)
1580 return PadRight (totalWidth, ' ');
1583 public unsafe String PadRight (int totalWidth, char paddingChar)
1585 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1587 if (totalWidth < 0)
1588 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1590 if (totalWidth < this.length)
1591 return this;
1592 if (totalWidth == 0)
1593 return Empty;
1595 String tmp = InternalAllocateStr (totalWidth);
1597 fixed (char* dest = tmp, src = this) {
1598 CharCopy (dest, src, length);
1600 char* padPos = dest + length;
1601 char* padTo = dest + totalWidth;
1602 while (padPos != padTo)
1603 *padPos++ = paddingChar;
1605 return tmp;
1608 public bool StartsWith (String value)
1610 if (value == null)
1611 throw new ArgumentNullException ("value");
1613 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1616 [ComVisible (false)]
1617 public bool StartsWith (string value, StringComparison comparisonType)
1619 if (value == null)
1620 throw new ArgumentNullException ("value");
1622 switch (comparisonType) {
1623 case StringComparison.CurrentCulture:
1624 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1625 case StringComparison.CurrentCultureIgnoreCase:
1626 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1627 case StringComparison.InvariantCulture:
1628 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1629 case StringComparison.InvariantCultureIgnoreCase:
1630 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1631 case StringComparison.Ordinal:
1632 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1633 case StringComparison.OrdinalIgnoreCase:
1634 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1635 default:
1636 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1637 throw new ArgumentException (msg, "comparisonType");
1641 [ComVisible (false)]
1642 public bool EndsWith (string value, StringComparison comparisonType)
1644 if (value == null)
1645 throw new ArgumentNullException ("value");
1647 switch (comparisonType) {
1648 case StringComparison.CurrentCulture:
1649 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1650 case StringComparison.CurrentCultureIgnoreCase:
1651 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1652 case StringComparison.InvariantCulture:
1653 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1654 case StringComparison.InvariantCultureIgnoreCase:
1655 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1656 case StringComparison.Ordinal:
1657 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1658 case StringComparison.OrdinalIgnoreCase:
1659 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1660 default:
1661 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1662 throw new ArgumentException (msg, "comparisonType");
1666 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1668 if (culture == null)
1669 culture = CultureInfo.CurrentCulture;
1671 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1674 // Following method is culture-insensitive
1675 public unsafe String Replace (char oldChar, char newChar)
1677 if (this.length == 0 || oldChar == newChar)
1678 return this;
1680 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1681 if (start_pos == -1)
1682 return this;
1684 if (start_pos < 4)
1685 start_pos = 0;
1687 string tmp = InternalAllocateStr (length);
1688 fixed (char* dest = tmp, src = &start_char) {
1689 if (start_pos != 0)
1690 CharCopy (dest, src, start_pos);
1692 char* end_ptr = dest + length;
1693 char* dest_ptr = dest + start_pos;
1694 char* src_ptr = src + start_pos;
1696 while (dest_ptr != end_ptr) {
1697 if (*src_ptr == oldChar)
1698 *dest_ptr = newChar;
1699 else
1700 *dest_ptr = *src_ptr;
1702 ++src_ptr;
1703 ++dest_ptr;
1706 return tmp;
1709 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1710 public String Replace (String oldValue, String newValue)
1712 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1713 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1715 if (oldValue == null)
1716 throw new ArgumentNullException ("oldValue");
1718 if (oldValue.Length == 0)
1719 throw new ArgumentException ("oldValue is the empty string.");
1721 if (this.Length == 0)
1722 return this;
1724 if (newValue == null)
1725 newValue = Empty;
1727 return ReplaceUnchecked (oldValue, newValue);
1730 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1732 if (oldValue.length > length)
1733 return this;
1734 if (oldValue.length == 1 && newValue.length == 1) {
1735 return Replace (oldValue[0], newValue[0]);
1736 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1737 // because the length of the result would be this.length and length calculation unneccesary
1740 const int maxValue = 200; // Allocate 800 byte maximum
1741 int* dat = stackalloc int[maxValue];
1742 fixed (char* source = this, replace = newValue) {
1743 int i = 0, count = 0;
1744 while (i < length) {
1745 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1746 if (found < 0)
1747 break;
1748 else {
1749 if (count < maxValue)
1750 dat[count++] = found;
1751 else
1752 return ReplaceFallback (oldValue, newValue, maxValue);
1754 i = found + oldValue.length;
1756 if (count == 0)
1757 return this;
1758 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1759 String tmp = InternalAllocateStr (nlen);
1761 int curPos = 0, lastReadPos = 0;
1762 fixed (char* dest = tmp) {
1763 for (int j = 0; j < count; j++) {
1764 int precopy = dat[j] - lastReadPos;
1765 CharCopy (dest + curPos, source + lastReadPos, precopy);
1766 curPos += precopy;
1767 lastReadPos = dat[j] + oldValue.length;
1768 CharCopy (dest + curPos, replace, newValue.length);
1769 curPos += newValue.length;
1771 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1773 return tmp;
1777 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1779 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1780 StringBuilder sb = new StringBuilder (lengthEstimate);
1781 for (int i = 0; i < length;) {
1782 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1783 if (found < 0) {
1784 sb.Append (SubstringUnchecked (i, length - i));
1785 break;
1787 sb.Append (SubstringUnchecked (i, found - i));
1788 sb.Append (newValue);
1789 i = found + oldValue.Length;
1791 return sb.ToString ();
1795 public unsafe String Remove (int startIndex, int count)
1797 if (startIndex < 0)
1798 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1799 if (count < 0)
1800 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1801 if (startIndex > this.length - count)
1802 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1804 String tmp = InternalAllocateStr (this.length - count);
1806 fixed (char *dest = tmp, src = this) {
1807 char *dst = dest;
1808 CharCopy (dst, src, startIndex);
1809 int skip = startIndex + count;
1810 dst += startIndex;
1811 CharCopy (dst, src + skip, length - skip);
1813 return tmp;
1816 public String ToLower ()
1818 return ToLower (CultureInfo.CurrentCulture);
1821 public String ToLower (CultureInfo culture)
1823 if (culture == null)
1824 throw new ArgumentNullException ("culture");
1826 if (culture.LCID == 0x007F) // Invariant
1827 return ToLowerInvariant ();
1829 return culture.TextInfo.ToLower (this);
1832 public unsafe String ToLowerInvariant ()
1834 if (length == 0)
1835 return Empty;
1837 string tmp = InternalAllocateStr (length);
1838 fixed (char* source = &start_char, dest = tmp) {
1840 char* destPtr = (char*)dest;
1841 char* sourcePtr = (char*)source;
1843 for (int n = 0; n < length; n++) {
1844 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1845 sourcePtr++;
1846 destPtr++;
1849 return tmp;
1852 public String ToUpper ()
1854 return ToUpper (CultureInfo.CurrentCulture);
1857 public String ToUpper (CultureInfo culture)
1859 if (culture == null)
1860 throw new ArgumentNullException ("culture");
1862 if (culture.LCID == 0x007F) // Invariant
1863 return ToUpperInvariant ();
1865 return culture.TextInfo.ToUpper (this);
1868 public unsafe String ToUpperInvariant ()
1870 if (length == 0)
1871 return Empty;
1873 string tmp = InternalAllocateStr (length);
1874 fixed (char* source = &start_char, dest = tmp) {
1876 char* destPtr = (char*)dest;
1877 char* sourcePtr = (char*)source;
1879 for (int n = 0; n < length; n++) {
1880 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1881 sourcePtr++;
1882 destPtr++;
1885 return tmp;
1888 public override String ToString ()
1890 return this;
1893 public String ToString (IFormatProvider provider)
1895 return this;
1898 public static String Format (String format, Object arg0)
1900 return Format (null, format, new Object[] {arg0});
1903 public static String Format (String format, Object arg0, Object arg1)
1905 return Format (null, format, new Object[] {arg0, arg1});
1908 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1910 return Format (null, format, new Object[] {arg0, arg1, arg2});
1913 public static string Format (string format, params object[] args)
1915 return Format (null, format, args);
1918 public static string Format (IFormatProvider provider, string format, params object[] args)
1920 StringBuilder b = FormatHelper (null, provider, format, args);
1921 return b.ToString ();
1924 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1926 if (format == null)
1927 throw new ArgumentNullException ("format");
1928 if (args == null)
1929 throw new ArgumentNullException ("args");
1931 if (result == null) {
1932 /* Try to approximate the size of result to avoid reallocations */
1933 int i, len;
1935 len = 0;
1936 for (i = 0; i < args.Length; ++i) {
1937 string s = args [i] as string;
1938 if (s != null)
1939 len += s.length;
1940 else
1941 break;
1943 if (i == args.Length)
1944 result = new StringBuilder (len + format.length);
1945 else
1946 result = new StringBuilder ();
1949 int ptr = 0;
1950 int start = ptr;
1951 while (ptr < format.length) {
1952 char c = format[ptr ++];
1954 if (c == '{') {
1955 result.Append (format, start, ptr - start - 1);
1957 // check for escaped open bracket
1959 if (format[ptr] == '{') {
1960 start = ptr ++;
1961 continue;
1964 // parse specifier
1966 int n, width;
1967 bool left_align;
1968 string arg_format;
1970 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1971 if (n >= args.Length)
1972 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1974 // format argument
1976 object arg = args[n];
1978 string str;
1979 ICustomFormatter formatter = null;
1980 if (provider != null)
1981 formatter = provider.GetFormat (typeof (ICustomFormatter))
1982 as ICustomFormatter;
1983 if (arg == null)
1984 str = Empty;
1985 else if (formatter != null)
1986 str = formatter.Format (arg_format, arg, provider);
1987 else if (arg is IFormattable)
1988 str = ((IFormattable)arg).ToString (arg_format, provider);
1989 else
1990 str = arg.ToString ();
1992 // pad formatted string and append to result
1994 if (width > str.length) {
1995 const char padchar = ' ';
1996 int padlen = width - str.length;
1998 if (left_align) {
1999 result.Append (str);
2000 result.Append (padchar, padlen);
2002 else {
2003 result.Append (padchar, padlen);
2004 result.Append (str);
2007 else
2008 result.Append (str);
2010 start = ptr;
2012 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2013 result.Append (format, start, ptr - start - 1);
2014 start = ptr ++;
2016 else if (c == '}') {
2017 throw new FormatException ("Input string was not in a correct format.");
2021 if (start < format.length)
2022 result.Append (format, start, format.Length - start);
2024 return result;
2027 public unsafe static String Copy (String str)
2029 if (str == null)
2030 throw new ArgumentNullException ("str");
2032 int length = str.length;
2034 String tmp = InternalAllocateStr (length);
2035 if (length != 0) {
2036 fixed (char *dest = tmp, src = str) {
2037 CharCopy (dest, src, length);
2040 return tmp;
2043 public static String Concat (Object arg0)
2045 if (arg0 == null)
2046 return Empty;
2048 return arg0.ToString ();
2051 public static String Concat (Object arg0, Object arg1)
2053 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2056 public static String Concat (Object arg0, Object arg1, Object arg2)
2058 string s1, s2, s3;
2059 if (arg0 == null)
2060 s1 = Empty;
2061 else
2062 s1 = arg0.ToString ();
2064 if (arg1 == null)
2065 s2 = Empty;
2066 else
2067 s2 = arg1.ToString ();
2069 if (arg2 == null)
2070 s3 = Empty;
2071 else
2072 s3 = arg2.ToString ();
2074 return Concat (s1, s2, s3);
2077 [CLSCompliant(false)]
2078 public static String Concat (Object arg0, Object arg1, Object arg2,
2079 Object arg3, __arglist)
2081 string s1, s2, s3, s4;
2083 if (arg0 == null)
2084 s1 = Empty;
2085 else
2086 s1 = arg0.ToString ();
2088 if (arg1 == null)
2089 s2 = Empty;
2090 else
2091 s2 = arg1.ToString ();
2093 if (arg2 == null)
2094 s3 = Empty;
2095 else
2096 s3 = arg2.ToString ();
2098 ArgIterator iter = new ArgIterator (__arglist);
2099 int argCount = iter.GetRemainingCount();
2101 StringBuilder sb = new StringBuilder ();
2102 if (arg3 != null)
2103 sb.Append (arg3.ToString ());
2105 for (int i = 0; i < argCount; i++) {
2106 TypedReference typedRef = iter.GetNextArg ();
2107 sb.Append (TypedReference.ToObject (typedRef));
2110 s4 = sb.ToString ();
2112 return Concat (s1, s2, s3, s4);
2115 public unsafe static String Concat (String str0, String str1)
2117 if (str0 == null || str0.Length == 0) {
2118 if (str1 == null || str1.Length == 0)
2119 return Empty;
2120 return str1;
2123 if (str1 == null || str1.Length == 0)
2124 return str0;
2126 String tmp = InternalAllocateStr (str0.length + str1.length);
2128 fixed (char *dest = tmp, src = str0)
2129 CharCopy (dest, src, str0.length);
2130 fixed (char *dest = tmp, src = str1)
2131 CharCopy (dest + str0.Length, src, str1.length);
2133 return tmp;
2136 public unsafe static String Concat (String str0, String str1, String str2)
2138 if (str0 == null || str0.Length == 0){
2139 if (str1 == null || str1.Length == 0){
2140 if (str2 == null || str2.Length == 0)
2141 return Empty;
2142 return str2;
2143 } else {
2144 if (str2 == null || str2.Length == 0)
2145 return str1;
2147 str0 = Empty;
2148 } else {
2149 if (str1 == null || str1.Length == 0){
2150 if (str2 == null || str2.Length == 0)
2151 return str0;
2152 else
2153 str1 = Empty;
2154 } else {
2155 if (str2 == null || str2.Length == 0)
2156 str2 = Empty;
2160 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2162 if (str0.Length != 0) {
2163 fixed (char *dest = tmp, src = str0) {
2164 CharCopy (dest, src, str0.length);
2167 if (str1.Length != 0) {
2168 fixed (char *dest = tmp, src = str1) {
2169 CharCopy (dest + str0.Length, src, str1.length);
2172 if (str2.Length != 0) {
2173 fixed (char *dest = tmp, src = str2) {
2174 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2178 return tmp;
2181 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2183 if (str0 == null && str1 == null && str2 == null && str3 == null)
2184 return Empty;
2186 if (str0 == null)
2187 str0 = Empty;
2188 if (str1 == null)
2189 str1 = Empty;
2190 if (str2 == null)
2191 str2 = Empty;
2192 if (str3 == null)
2193 str3 = Empty;
2195 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2197 if (str0.Length != 0) {
2198 fixed (char *dest = tmp, src = str0) {
2199 CharCopy (dest, src, str0.length);
2202 if (str1.Length != 0) {
2203 fixed (char *dest = tmp, src = str1) {
2204 CharCopy (dest + str0.Length, src, str1.length);
2207 if (str2.Length != 0) {
2208 fixed (char *dest = tmp, src = str2) {
2209 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2212 if (str3.Length != 0) {
2213 fixed (char *dest = tmp, src = str3) {
2214 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2218 return tmp;
2221 public static String Concat (params Object[] args)
2223 if (args == null)
2224 throw new ArgumentNullException ("args");
2226 int argLen = args.Length;
2227 if (argLen == 0)
2228 return Empty;
2230 string [] strings = new string [argLen];
2231 int len = 0;
2232 for (int i = 0; i < argLen; i++) {
2233 if (args[i] != null) {
2234 strings[i] = args[i].ToString ();
2235 len += strings[i].length;
2239 return ConcatInternal (strings, len);
2242 public static String Concat (params String[] values)
2244 if (values == null)
2245 throw new ArgumentNullException ("values");
2247 int len = 0;
2248 for (int i = 0; i < values.Length; i++) {
2249 String s = values[i];
2250 if (s != null)
2251 len += s.length;
2254 return ConcatInternal (values, len);
2257 private static unsafe String ConcatInternal (String[] values, int length)
2259 if (length == 0)
2260 return Empty;
2262 String tmp = InternalAllocateStr (length);
2264 fixed (char* dest = tmp) {
2265 int pos = 0;
2266 for (int i = 0; i < values.Length; i++) {
2267 String source = values[i];
2268 if (source != null) {
2269 fixed (char* src = source) {
2270 CharCopy (dest + pos, src, source.length);
2272 pos += source.Length;
2276 return tmp;
2279 public unsafe String Insert (int startIndex, String value)
2281 if (value == null)
2282 throw new ArgumentNullException ("value");
2284 if (startIndex < 0 || startIndex > this.length)
2285 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2287 if (value.Length == 0)
2288 return this;
2289 if (this.Length == 0)
2290 return value;
2291 String tmp = InternalAllocateStr (this.length + value.length);
2293 fixed (char *dest = tmp, src = this, val = value) {
2294 char *dst = dest;
2295 CharCopy (dst, src, startIndex);
2296 dst += startIndex;
2297 CharCopy (dst, val, value.length);
2298 dst += value.length;
2299 CharCopy (dst, src + startIndex, length - startIndex);
2301 return tmp;
2304 public static string Intern (string str)
2306 if (str == null)
2307 throw new ArgumentNullException ("str");
2309 return InternalIntern (str);
2312 public static string IsInterned (string str)
2314 if (str == null)
2315 throw new ArgumentNullException ("str");
2317 return InternalIsInterned (str);
2320 #if NET_4_0 || MOONLIGHT || MOBILE
2321 public static string Join (string separator, params string [] value)
2322 #else
2323 public static string Join (string separator, string [] value)
2324 #endif
2326 if (value == null)
2327 throw new ArgumentNullException ("value");
2328 if (separator == null)
2329 separator = Empty;
2331 return JoinUnchecked (separator, value, 0, value.Length);
2334 public static string Join (string separator, string[] value, int startIndex, int count)
2336 if (value == null)
2337 throw new ArgumentNullException ("value");
2338 if (startIndex < 0)
2339 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2340 if (count < 0)
2341 throw new ArgumentOutOfRangeException ("count", "< 0");
2342 if (startIndex > value.Length - count)
2343 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2345 if (startIndex == value.Length)
2346 return Empty;
2347 if (separator == null)
2348 separator = Empty;
2350 return JoinUnchecked (separator, value, startIndex, count);
2353 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2355 // Unchecked parameters
2356 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2357 // separator and value must not be null
2359 int length = 0;
2360 int maxIndex = startIndex + count;
2361 // Precount the number of characters that the resulting string will have
2362 for (int i = startIndex; i < maxIndex; i++) {
2363 String s = value[i];
2364 if (s != null)
2365 length += s.length;
2367 length += separator.length * (count - 1);
2368 if (length <= 0)
2369 return Empty;
2371 String tmp = InternalAllocateStr (length);
2373 maxIndex--;
2374 fixed (char* dest = tmp, sepsrc = separator) {
2375 // Copy each string from value except the last one and add a separator for each
2376 int pos = 0;
2377 for (int i = startIndex; i < maxIndex; i++) {
2378 String source = value[i];
2379 if (source != null) {
2380 if (source.Length > 0) {
2381 fixed (char* src = source)
2382 CharCopy (dest + pos, src, source.Length);
2383 pos += source.Length;
2386 if (separator.Length > 0) {
2387 CharCopy (dest + pos, sepsrc, separator.Length);
2388 pos += separator.Length;
2391 // Append last string that does not get an additional separator
2392 String sourceLast = value[maxIndex];
2393 if (sourceLast != null) {
2394 if (sourceLast.Length > 0) {
2395 fixed (char* src = sourceLast)
2396 CharCopy (dest + pos, src, sourceLast.Length);
2400 return tmp;
2403 bool IConvertible.ToBoolean (IFormatProvider provider)
2405 return Convert.ToBoolean (this, provider);
2408 byte IConvertible.ToByte (IFormatProvider provider)
2410 return Convert.ToByte (this, provider);
2413 char IConvertible.ToChar (IFormatProvider provider)
2415 return Convert.ToChar (this, provider);
2418 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2420 return Convert.ToDateTime (this, provider);
2423 decimal IConvertible.ToDecimal (IFormatProvider provider)
2425 return Convert.ToDecimal (this, provider);
2428 double IConvertible.ToDouble (IFormatProvider provider)
2430 return Convert.ToDouble (this, provider);
2433 short IConvertible.ToInt16 (IFormatProvider provider)
2435 return Convert.ToInt16 (this, provider);
2438 int IConvertible.ToInt32 (IFormatProvider provider)
2440 return Convert.ToInt32 (this, provider);
2443 long IConvertible.ToInt64 (IFormatProvider provider)
2445 return Convert.ToInt64 (this, provider);
2448 sbyte IConvertible.ToSByte (IFormatProvider provider)
2450 return Convert.ToSByte (this, provider);
2453 float IConvertible.ToSingle (IFormatProvider provider)
2455 return Convert.ToSingle (this, provider);
2458 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2460 if (targetType == null)
2461 throw new ArgumentNullException ("type");
2462 return Convert.ToType (this, targetType, provider, false);
2465 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2467 return Convert.ToUInt16 (this, provider);
2470 uint IConvertible.ToUInt32 (IFormatProvider provider)
2472 return Convert.ToUInt32 (this, provider);
2475 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2477 return Convert.ToUInt64 (this, provider);
2480 public int Length {
2481 get {
2482 return length;
2486 public CharEnumerator GetEnumerator ()
2488 return new CharEnumerator (this);
2491 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2493 return new CharEnumerator (this);
2496 IEnumerator IEnumerable.GetEnumerator ()
2498 return new CharEnumerator (this);
2501 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2502 out bool left_align, out string format)
2504 int max = str.Length;
2506 // parses format specifier of form:
2507 // N,[\ +[-]M][:F]}
2509 // where:
2510 // N = argument number (non-negative integer)
2512 n = ParseDecimal (str, ref ptr);
2513 if (n < 0)
2514 throw new FormatException ("Input string was not in a correct format.");
2516 // M = width (non-negative integer)
2518 if (ptr < max && str[ptr] == ',') {
2519 // White space between ',' and number or sign.
2520 ++ptr;
2521 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2522 ++ptr;
2523 int start = ptr;
2525 format = str.Substring (start, ptr - start);
2527 left_align = (ptr < max && str [ptr] == '-');
2528 if (left_align)
2529 ++ ptr;
2531 width = ParseDecimal (str, ref ptr);
2532 if (width < 0)
2533 throw new FormatException ("Input string was not in a correct format.");
2535 else {
2536 width = 0;
2537 left_align = false;
2538 format = Empty;
2541 // F = argument format (string)
2543 if (ptr < max && str[ptr] == ':') {
2544 int start = ++ ptr;
2545 while (ptr < max && str[ptr] != '}')
2546 ++ ptr;
2548 format += str.Substring (start, ptr - start);
2550 else
2551 format = null;
2553 if ((ptr >= max) || str[ptr ++] != '}')
2554 throw new FormatException ("Input string was not in a correct format.");
2557 private static int ParseDecimal (string str, ref int ptr)
2559 int p = ptr;
2560 int n = 0;
2561 int max = str.Length;
2563 while (p < max) {
2564 char c = str[p];
2565 if (c < '0' || '9' < c)
2566 break;
2568 n = n * 10 + c - '0';
2569 ++ p;
2572 if (p == ptr || p == max)
2573 return -1;
2575 ptr = p;
2576 return n;
2579 internal unsafe void InternalSetChar (int idx, char val)
2581 if ((uint) idx >= (uint) Length)
2582 throw new ArgumentOutOfRangeException ("idx");
2584 fixed (char * pStr = &start_char)
2586 pStr [idx] = val;
2590 internal unsafe void InternalSetLength (int newLength)
2592 if (newLength > length)
2593 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2595 // zero terminate, we can pass string objects directly via pinvoke
2596 // we also zero the rest of the string, since the new GC needs to be
2597 // able to handle the changing size (it will skip the 0 bytes).
2598 fixed (char * pStr = &start_char) {
2599 char *p = pStr + newLength;
2600 char *end = pStr + length;
2601 while (p < end) {
2602 p [0] = '\0';
2603 p++;
2606 length = newLength;
2609 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2610 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2611 public unsafe override int GetHashCode ()
2613 fixed (char * c = this) {
2614 char * cc = c;
2615 char * end = cc + length - 1;
2616 int h = 0;
2617 for (;cc < end; cc += 2) {
2618 h = (h << 5) - h + *cc;
2619 h = (h << 5) - h + cc [1];
2621 ++end;
2622 if (cc < end)
2623 h = (h << 5) - h + *cc;
2624 return h;
2628 #if MOONLIGHT || MOBILE || NET_4_0
2629 [ComVisible(false)]
2630 public static string Concat (IEnumerable<string> values)
2632 if (values == null)
2633 throw new ArgumentNullException ("values");
2635 var stringList = new List<string> ();
2636 int len = 0;
2637 foreach (var v in values){
2638 if (v == null)
2639 continue;
2640 len += v.Length;
2641 stringList.Add (v);
2643 return ConcatInternal (stringList.ToArray (), len);
2646 [ComVisibleAttribute(false)]
2647 public static string Concat<T> (IEnumerable<T> values)
2649 if (values == null)
2650 throw new ArgumentNullException ("values");
2652 var stringList = new List<string> ();
2653 int len = 0;
2654 foreach (var v in values){
2655 string sr = v.ToString ();
2656 len += sr.Length;
2657 stringList.Add (sr);
2659 return ConcatInternal (stringList.ToArray (), len);
2662 [ComVisibleAttribute(false)]
2663 public static string Join (string separator, IEnumerable<string> values)
2665 if (separator == null)
2666 return Concat (values);
2668 if (values == null)
2669 throw new ArgumentNullException ("values");
2671 var stringList = new List<string> ();
2672 foreach (var v in values)
2673 stringList.Add (v);
2675 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2678 [ComVisibleAttribute(false)]
2679 public static string Join (string separator, params object [] values)
2681 if (separator == null)
2682 return Concat (values);
2684 if (values == null)
2685 throw new ArgumentNullException ("values");
2687 var strCopy = new string [values.Length];
2688 int i = 0;
2689 foreach (var v in values)
2690 strCopy [i++] = v.ToString ();
2692 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2695 [ComVisible (false)]
2696 public static string Join<T> (string separator, IEnumerable<T> values)
2698 if (separator == null)
2699 return Concat<T> (values);
2701 if (values == null)
2702 throw new ArgumentNullException ("values");
2704 var stringList = new List<string> ();
2705 foreach (var v in values)
2706 stringList.Add (v.ToString ());
2708 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2711 public static bool IsNullOrWhiteSpace (string value)
2712 #else
2713 internal static bool IsNullOrWhiteSpace (string value)
2714 #endif
2716 if ((value == null) || (value.Length == 0))
2717 return true;
2718 foreach (char c in value)
2719 if (!Char.IsWhiteSpace (c))
2720 return false;
2721 return true;
2724 internal unsafe int GetCaseInsensitiveHashCode ()
2726 fixed (char * c = this) {
2727 char * cc = c;
2728 char * end = cc + length - 1;
2729 int h = 0;
2730 for (;cc < end; cc += 2) {
2731 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2732 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2734 ++end;
2735 if (cc < end)
2736 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2737 return h;
2741 // Certain constructors are redirected to CreateString methods with
2742 // matching argument list. The this pointer should not be used.
2743 #pragma warning disable 169
2744 private unsafe String CreateString (sbyte* value)
2746 if (value == null)
2747 return Empty;
2749 byte* bytes = (byte*) value;
2750 int length = 0;
2752 try {
2753 while (bytes++ [0] != 0)
2754 length++;
2755 } catch (NullReferenceException) {
2756 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2757 } catch (AccessViolationException) {
2758 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2761 return CreateString (value, 0, length, null);
2764 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2766 return CreateString (value, startIndex, length, null);
2769 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2771 if (length < 0)
2772 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2773 if (startIndex < 0)
2774 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2775 if (value + startIndex < value)
2776 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2778 bool isDefaultEncoding;
2780 if (isDefaultEncoding = (enc == null)) {
2781 if (value == null)
2782 throw new ArgumentNullException ("value");
2783 if (length == 0)
2784 return Empty;
2786 enc = Encoding.Default;
2789 byte [] bytes = new byte [length];
2791 if (length != 0)
2792 fixed (byte* bytePtr = bytes)
2793 try {
2794 memcpy (bytePtr, (byte*) (value + startIndex), length);
2795 } catch (NullReferenceException) {
2796 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2797 } catch (AccessViolationException) {
2798 if (!isDefaultEncoding)
2799 throw;
2801 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2804 // GetString () is called even when length == 0
2805 return enc.GetString (bytes);
2808 unsafe string CreateString (char *value)
2810 if (value == null)
2811 return Empty;
2812 char *p = value;
2813 int i = 0;
2814 while (*p != 0) {
2815 ++i;
2816 ++p;
2818 string result = InternalAllocateStr (i);
2820 if (i != 0) {
2821 fixed (char *dest = result) {
2822 CharCopy (dest, value, i);
2825 return result;
2828 unsafe string CreateString (char *value, int startIndex, int length)
2830 if (length == 0)
2831 return Empty;
2832 if (value == null)
2833 throw new ArgumentNullException ("value");
2834 if (startIndex < 0)
2835 throw new ArgumentOutOfRangeException ("startIndex");
2836 if (length < 0)
2837 throw new ArgumentOutOfRangeException ("length");
2839 string result = InternalAllocateStr (length);
2841 fixed (char *dest = result) {
2842 CharCopy (dest, value + startIndex, length);
2844 return result;
2847 unsafe string CreateString (char [] val, int startIndex, int length)
2849 if (val == null)
2850 throw new ArgumentNullException ("value");
2851 if (startIndex < 0)
2852 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2853 if (length < 0)
2854 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2855 if (startIndex > val.Length - length)
2856 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2857 if (length == 0)
2858 return Empty;
2860 string result = InternalAllocateStr (length);
2862 fixed (char *dest = result, src = val) {
2863 CharCopy (dest, src + startIndex, length);
2865 return result;
2868 unsafe string CreateString (char [] val)
2870 if (val == null || val.Length == 0)
2871 return Empty;
2872 string result = InternalAllocateStr (val.Length);
2874 fixed (char *dest = result, src = val) {
2875 CharCopy (dest, src, val.Length);
2877 return result;
2880 unsafe string CreateString (char c, int count)
2882 if (count < 0)
2883 throw new ArgumentOutOfRangeException ("count");
2884 if (count == 0)
2885 return Empty;
2886 string result = InternalAllocateStr (count);
2887 fixed (char *dest = result) {
2888 char *p = dest;
2889 char *end = p + count;
2890 while (p < end) {
2891 *p = c;
2892 p++;
2895 return result;
2897 #pragma warning restore 169
2899 /* helpers used by the runtime as well as above or eslewhere in corlib */
2900 internal static unsafe void memset (byte *dest, int val, int len)
2902 if (len < 8) {
2903 while (len != 0) {
2904 *dest = (byte)val;
2905 ++dest;
2906 --len;
2908 return;
2910 if (val != 0) {
2911 val = val | (val << 8);
2912 val = val | (val << 16);
2914 // align to 4
2915 int rest = (int)dest & 3;
2916 if (rest != 0) {
2917 rest = 4 - rest;
2918 len -= rest;
2919 do {
2920 *dest = (byte)val;
2921 ++dest;
2922 --rest;
2923 } while (rest != 0);
2925 while (len >= 16) {
2926 ((int*)dest) [0] = val;
2927 ((int*)dest) [1] = val;
2928 ((int*)dest) [2] = val;
2929 ((int*)dest) [3] = val;
2930 dest += 16;
2931 len -= 16;
2933 while (len >= 4) {
2934 ((int*)dest) [0] = val;
2935 dest += 4;
2936 len -= 4;
2938 // tail bytes
2939 while (len > 0) {
2940 *dest = (byte)val;
2941 dest++;
2942 len--;
2946 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2947 /*while (size >= 32) {
2948 // using long is better than int and slower than double
2949 // FIXME: enable this only on correct alignment or on platforms
2950 // that can tolerate unaligned reads/writes of doubles
2951 ((double*)dest) [0] = ((double*)src) [0];
2952 ((double*)dest) [1] = ((double*)src) [1];
2953 ((double*)dest) [2] = ((double*)src) [2];
2954 ((double*)dest) [3] = ((double*)src) [3];
2955 dest += 32;
2956 src += 32;
2957 size -= 32;
2959 while (size >= 16) {
2960 ((int*)dest) [0] = ((int*)src) [0];
2961 ((int*)dest) [1] = ((int*)src) [1];
2962 ((int*)dest) [2] = ((int*)src) [2];
2963 ((int*)dest) [3] = ((int*)src) [3];
2964 dest += 16;
2965 src += 16;
2966 size -= 16;
2968 while (size >= 4) {
2969 ((int*)dest) [0] = ((int*)src) [0];
2970 dest += 4;
2971 src += 4;
2972 size -= 4;
2974 while (size > 0) {
2975 ((byte*)dest) [0] = ((byte*)src) [0];
2976 dest += 1;
2977 src += 1;
2978 --size;
2981 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2982 while (size >= 8) {
2983 ((short*)dest) [0] = ((short*)src) [0];
2984 ((short*)dest) [1] = ((short*)src) [1];
2985 ((short*)dest) [2] = ((short*)src) [2];
2986 ((short*)dest) [3] = ((short*)src) [3];
2987 dest += 8;
2988 src += 8;
2989 size -= 8;
2991 while (size >= 2) {
2992 ((short*)dest) [0] = ((short*)src) [0];
2993 dest += 2;
2994 src += 2;
2995 size -= 2;
2997 if (size > 0)
2998 ((byte*)dest) [0] = ((byte*)src) [0];
3000 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3001 while (size >= 8) {
3002 ((byte*)dest) [0] = ((byte*)src) [0];
3003 ((byte*)dest) [1] = ((byte*)src) [1];
3004 ((byte*)dest) [2] = ((byte*)src) [2];
3005 ((byte*)dest) [3] = ((byte*)src) [3];
3006 ((byte*)dest) [4] = ((byte*)src) [4];
3007 ((byte*)dest) [5] = ((byte*)src) [5];
3008 ((byte*)dest) [6] = ((byte*)src) [6];
3009 ((byte*)dest) [7] = ((byte*)src) [7];
3010 dest += 8;
3011 src += 8;
3012 size -= 8;
3014 while (size >= 2) {
3015 ((byte*)dest) [0] = ((byte*)src) [0];
3016 ((byte*)dest) [1] = ((byte*)src) [1];
3017 dest += 2;
3018 src += 2;
3019 size -= 2;
3021 if (size > 0)
3022 ((byte*)dest) [0] = ((byte*)src) [0];
3025 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3026 // FIXME: if pointers are not aligned, try to align them
3027 // so a faster routine can be used. Handle the case where
3028 // the pointers can't be reduced to have the same alignment
3029 // (just ignore the issue on x86?)
3030 if ((((int)dest | (int)src) & 3) != 0) {
3031 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3032 dest [0] = src [0];
3033 ++dest;
3034 ++src;
3035 --size;
3037 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3038 ((short*)dest) [0] = ((short*)src) [0];
3039 dest += 2;
3040 src += 2;
3041 size -= 2;
3043 if ((((int)dest | (int)src) & 1) != 0) {
3044 memcpy1 (dest, src, size);
3045 return;
3047 if ((((int)dest | (int)src) & 2) != 0) {
3048 memcpy2 (dest, src, size);
3049 return;
3052 memcpy4 (dest, src, size);
3055 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3056 // Same rules as for memcpy, but with the premise that
3057 // chars can only be aligned to even addresses if their
3058 // enclosing types are correctly aligned
3059 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3060 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3061 ((short*)dest) [0] = ((short*)src) [0];
3062 dest++;
3063 src++;
3064 count--;
3066 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3067 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3068 return;
3071 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3074 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3076 dest += count;
3077 src += count;
3078 for (int i = count; i > 0; i--) {
3079 dest--;
3080 src--;
3081 *dest = *src;
3085 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3087 fixed (char* dest = target, src = source)
3088 CharCopy (dest + targetIndex, src + sourceIndex, count);
3091 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3093 fixed (char* dest = target, src = source)
3094 CharCopy (dest + targetIndex, src + sourceIndex, count);
3097 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3098 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3100 fixed (char* dest = target, src = source)
3101 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3104 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3105 unsafe public extern String (char *value);
3107 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3108 unsafe public extern String (char *value, int startIndex, int length);
3110 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3111 unsafe public extern String (sbyte *value);
3113 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3114 unsafe public extern String (sbyte *value, int startIndex, int length);
3116 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3117 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3119 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3120 public extern String (char [] value, int startIndex, int length);
3122 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3123 public extern String (char [] value);
3125 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3126 public extern String (char c, int count);
3128 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
3129 //private extern String[] InternalSplit (char[] separator, int count, int options);
3131 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3132 internal extern static String InternalAllocateStr (int length);
3134 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3135 private extern static string InternalIntern (string str);
3137 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3138 private extern static string InternalIsInterned (string str);
3140 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3141 private extern static int GetLOSLimit ();