Revert "Revert "[corlib] StringBuilder from reference sourcers""
[mono-project.git] / mcs / class / corlib / System / String.cs
blobca183385132f872af210c1d7c521b292c80d9680
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)
14 // Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 // This class contains all implementation for culture-insensitive methods.
38 // Culture-sensitive methods are implemented in the System.Globalization or
39 // Mono.Globalization namespace.
41 // Ensure that argument checks on methods don't overflow
44 using System.Text;
45 using System.Collections;
46 using System.Globalization;
47 using System.Runtime.CompilerServices;
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
54 using System.Diagnostics.Contracts;
56 namespace System
58 [Serializable]
59 [ComVisible (true)]
60 [StructLayout (LayoutKind.Sequential)]
61 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
63 [NonSerialized] private int length;
64 [NonSerialized] private char start_char;
66 public static readonly String Empty = "";
68 internal static readonly int LOS_limit = GetLOSLimit ();
70 internal static bool LegacyMode {
71 get {
72 return false;
76 public static unsafe bool Equals (string a, string b)
78 if ((a as object) == (b as object))
79 return true;
81 if (a == null || b == null)
82 return false;
84 int len = a.length;
86 if (len != b.length)
87 return false;
89 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
90 char* s1_ptr = s1;
91 char* s2_ptr = s2;
93 while (len >= 8) {
94 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
95 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
96 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
97 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
98 return false;
100 s1_ptr += 8;
101 s2_ptr += 8;
102 len -= 8;
105 if (len >= 4) {
106 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
107 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
108 return false;
110 s1_ptr += 4;
111 s2_ptr += 4;
112 len -= 4;
115 if (len > 1) {
116 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
117 return false;
119 s1_ptr += 2;
120 s2_ptr += 2;
121 len -= 2;
124 return len == 0 || *s1_ptr == *s2_ptr;
128 public static bool operator == (String a, String b)
130 return Equals (a, b);
133 public static bool operator != (String a, String b)
135 return !Equals (a, b);
138 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
139 public override bool Equals (Object obj)
141 return Equals (this, obj as String);
144 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
145 public bool Equals (String value)
147 return Equals (this, value);
150 [IndexerName ("Chars")]
151 public unsafe char this [int index] {
152 get {
153 if (index < 0 || index >= length)
154 throw new IndexOutOfRangeException ();
155 fixed (char* c = &start_char)
156 return c[index];
160 public Object Clone ()
162 return this;
165 public TypeCode GetTypeCode ()
167 return TypeCode.String;
170 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
172 if (destination == null)
173 throw new ArgumentNullException ("destination");
174 if (sourceIndex < 0)
175 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
176 if (destinationIndex < 0)
177 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
178 if (count < 0)
179 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
180 if (sourceIndex > Length - count)
181 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
182 if (destinationIndex > destination.Length - count)
183 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
185 fixed (char* dest = destination, src = this)
186 CharCopy (dest + destinationIndex, src + sourceIndex, count);
189 public char[] ToCharArray ()
191 return ToCharArray (0, length);
194 public unsafe char[] ToCharArray (int startIndex, int length)
196 if (startIndex < 0)
197 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
198 if (length < 0)
199 throw new ArgumentOutOfRangeException ("length", "< 0");
200 if (startIndex > this.length - length)
201 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
203 char[] tmp = new char [length];
204 fixed (char* dest = tmp, src = this)
205 CharCopy (dest, src + startIndex, length);
206 return tmp;
209 public String [] Split (params char [] separator)
211 return Split (separator, int.MaxValue, 0);
214 public String[] Split (char[] separator, int count)
216 return Split (separator, count, 0);
219 [ComVisible (false)]
220 public String[] Split (char[] separator, StringSplitOptions options)
222 return Split (separator, Int32.MaxValue, options);
225 [ComVisible (false)]
226 public String[] Split (char[] separator, int count, StringSplitOptions options)
228 if (count < 0)
229 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
230 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
231 throw new ArgumentException ("Illegal enum value: " + options + ".");
233 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
234 return EmptyArray<string>.Value;
236 if (count <= 1) {
237 return count == 0 ?
238 EmptyArray<string>.Value :
239 new String[1] { this };
242 return SplitByCharacters (separator, count, options != 0);
245 [ComVisible (false)]
246 public String[] Split (string[] separator, StringSplitOptions options)
248 return Split (separator, Int32.MaxValue, options);
251 [ComVisible (false)]
252 public String[] Split (string[] separator, int count, StringSplitOptions options)
254 if (count < 0)
255 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
256 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
257 throw new ArgumentException ("Illegal enum value: " + options + ".");
259 if (count <= 1) {
260 return count == 0 ?
261 EmptyArray<string>.Value :
262 new String[1] { this };
265 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
267 if (separator == null || separator.Length == 0)
268 return SplitByCharacters (null, count, removeEmpty);
270 if (Length == 0 && removeEmpty)
271 return EmptyArray<string>.Value;
273 List<String> arr = new List<String> ();
275 int pos = 0;
276 int matchCount = 0;
277 while (pos < this.Length) {
278 int matchIndex = -1;
279 int matchPos = Int32.MaxValue;
281 // Find the first position where any of the separators matches
282 for (int i = 0; i < separator.Length; ++i) {
283 string sep = separator [i];
284 if (sep == null || sep.Length == 0)
285 continue;
287 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
288 if (match >= 0 && match < matchPos) {
289 matchIndex = i;
290 matchPos = match;
294 if (matchIndex == -1)
295 break;
297 if (!(matchPos == pos && removeEmpty)) {
298 if (arr.Count == count - 1)
299 break;
300 arr.Add (this.Substring (pos, matchPos - pos));
303 pos = matchPos + separator [matchIndex].Length;
305 matchCount ++;
308 if (matchCount == 0)
309 return new String [] { this };
311 // string contained only separators
312 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
313 return EmptyArray<string>.Value;
315 if (!(removeEmpty && pos == this.Length))
316 arr.Add (this.Substring (pos));
318 return arr.ToArray ();
321 // .NET 2.0 compatibility only
323 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
326 int[] split_points = null;
327 int total_points = 0;
328 --count;
330 if (sep == null || sep.Length == 0) {
331 fixed (char* src = this) {
332 char* src_ptr = src;
333 int len = Length;
335 while (len > 0) {
336 if (char.IsWhiteSpace (*src_ptr++)) {
337 if (split_points == null) {
338 split_points = new int[8];
339 } else if (split_points.Length == total_points) {
340 Array.Resize (ref split_points, split_points.Length * 2);
343 split_points[total_points++] = Length - len;
344 if (total_points == count && !removeEmpty)
345 break;
347 --len;
350 } else {
351 fixed (char* src = this) {
352 fixed (char* sep_src = sep) {
353 char* src_ptr = src;
354 char* sep_ptr_end = sep_src + sep.Length;
355 int len = Length;
356 while (len > 0) {
357 char* sep_ptr = sep_src;
358 do {
359 if (*sep_ptr++ == *src_ptr) {
360 if (split_points == null) {
361 split_points = new int[8];
362 } else if (split_points.Length == total_points) {
363 Array.Resize (ref split_points, split_points.Length * 2);
366 split_points[total_points++] = Length - len;
367 if (total_points == count && !removeEmpty)
368 len = 0;
370 break;
372 } while (sep_ptr != sep_ptr_end);
374 ++src_ptr;
375 --len;
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 cannot exceed length of string.");
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 fixed (char* src = this) {
537 while (pos != target) {
538 if (!char.IsWhiteSpace (src[pos]))
539 return pos;
541 pos += change;
544 return pos;
547 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
549 fixed (char* tablePtr = table, thisPtr = this) {
550 while (pos != target) {
551 char c = thisPtr[pos];
552 int x = 0;
553 while (x < table.Length) {
554 if (c == tablePtr[x])
555 break;
556 x++;
558 if (x == table.Length)
559 return pos;
560 pos += change;
563 return pos;
566 public static int Compare (String strA, String strB)
568 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
571 public static int Compare (String strA, String strB, bool ignoreCase)
573 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
576 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
578 if (culture == null)
579 throw new ArgumentNullException ("culture");
581 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
584 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
586 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
589 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
591 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
594 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
596 if (culture == null)
597 throw new ArgumentNullException ("culture");
599 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
600 throw new ArgumentOutOfRangeException ();
602 if (length == 0)
603 return 0;
605 if (strA == null) {
606 if (strB == null) {
607 return 0;
608 } else {
609 return -1;
612 else if (strB == null) {
613 return 1;
616 CompareOptions compopts;
618 if (ignoreCase)
619 compopts = CompareOptions.IgnoreCase;
620 else
621 compopts = CompareOptions.None;
623 // Need to cap the requested length to the
624 // length of the string, because
625 // CompareInfo.Compare will insist that length
626 // <= (string.Length - offset)
628 int len1 = length;
629 int len2 = length;
631 if (length > (strA.Length - indexA)) {
632 len1 = strA.Length - indexA;
635 if (length > (strB.Length - indexB)) {
636 len2 = strB.Length - indexB;
639 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
640 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
643 public static int Compare (string strA, string strB, StringComparison comparisonType)
645 switch (comparisonType) {
646 case StringComparison.CurrentCulture:
647 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
648 case StringComparison.CurrentCultureIgnoreCase:
649 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
650 case StringComparison.InvariantCulture:
651 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
652 case StringComparison.InvariantCultureIgnoreCase:
653 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
654 case StringComparison.Ordinal:
655 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
656 case StringComparison.OrdinalIgnoreCase:
657 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
658 default:
659 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
660 throw new ArgumentException (msg, "comparisonType");
664 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
666 switch (comparisonType) {
667 case StringComparison.CurrentCulture:
668 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
669 case StringComparison.CurrentCultureIgnoreCase:
670 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
671 case StringComparison.InvariantCulture:
672 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
673 case StringComparison.InvariantCultureIgnoreCase:
674 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
675 case StringComparison.Ordinal:
676 return CompareOrdinal (strA, indexA, strB, indexB, length);
677 case StringComparison.OrdinalIgnoreCase:
678 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
679 default:
680 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
681 throw new ArgumentException (msg, "comparisonType");
685 public static bool Equals (string a, string b, StringComparison comparisonType)
687 return Compare (a, b, comparisonType) == 0;
690 public bool Equals (string value, StringComparison comparisonType)
692 return Compare (value, this, comparisonType) == 0;
695 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
697 if (culture == null)
698 throw new ArgumentNullException ("culture");
700 return culture.CompareInfo.Compare (strA, strB, options);
703 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
705 if (culture == null)
706 throw new ArgumentNullException ("culture");
708 int len1 = length;
709 int len2 = length;
711 if (length > (strA.Length - indexA))
712 len1 = strA.Length - indexA;
714 if (length > (strB.Length - indexB))
715 len2 = strB.Length - indexB;
717 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
720 public int CompareTo (Object value)
722 if (value == null)
723 return 1;
725 if (!(value is String))
726 throw new ArgumentException ();
728 return String.Compare (this, (String) value);
731 public int CompareTo (String strB)
733 if (strB == null)
734 return 1;
736 return Compare (this, strB);
739 public static int CompareOrdinal (String strA, String strB)
741 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
744 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
746 if (strA != null && strB != null)
748 if (indexA > strA.Length || indexA < 0)
749 throw new ArgumentOutOfRangeException ("indexA");
750 if (indexB > strB.Length || indexB < 0)
751 throw new ArgumentOutOfRangeException ("indexB");
752 if (length < 0)
753 throw new ArgumentOutOfRangeException ("length");
756 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
759 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
761 if (strA != null && strB != null)
763 if (indexA > strA.Length || indexA < 0)
764 throw new ArgumentOutOfRangeException ("indexA");
765 if (indexB > strB.Length || indexB < 0)
766 throw new ArgumentOutOfRangeException ("indexB");
767 if (length < 0)
768 throw new ArgumentOutOfRangeException ("length");
771 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
774 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
776 if (strA == null) {
777 return strB == null ? 0 : -1;
779 if (strB == null) {
780 return 1;
782 int lengthA = Math.Min (lenA, strA.Length - indexA);
783 int lengthB = Math.Min (lenB, strB.Length - indexB);
785 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
786 return 0;
788 fixed (char* aptr = strA, bptr = strB) {
789 char* ap = aptr + indexA;
790 char* end = ap + Math.Min (lengthA, lengthB);
791 char* bp = bptr + indexB;
792 while (ap < end) {
793 if (*ap != *bp)
794 return *ap - *bp;
795 ap++;
796 bp++;
798 return lengthA - lengthB;
803 // Fastest method for internal case insensitive comparison
805 internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
807 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
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 return strB == null ? 0 : -1;
816 if (strB == null) {
817 return 1;
819 int lengthA = Math.Min (lenA, strA.Length - indexA);
820 int lengthB = Math.Min (lenB, strB.Length - indexB);
822 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
823 return 0;
825 fixed (char* aptr = strA, bptr = strB) {
826 char* ap = aptr + indexA;
827 char* end = ap + Math.Min (lengthA, lengthB);
828 char* bp = bptr + indexB;
829 while (ap < end) {
830 if (*ap != *bp) {
831 char c1 = Char.ToUpperInvariant (*ap);
832 char c2 = Char.ToUpperInvariant (*bp);
833 if (c1 != c2)
834 return c1 - c2;
836 ap++;
837 bp++;
839 return lengthA - lengthB;
843 public bool EndsWith (String value)
845 if (value == null)
846 throw new ArgumentNullException ("value");
848 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
851 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
853 if (value == null)
854 throw new ArgumentNullException ("value");
855 if (culture == null)
856 culture = CultureInfo.CurrentCulture;
858 return culture.CompareInfo.IsSuffix (this, value,
859 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
862 // Following methods are culture-insensitive
863 public int IndexOfAny (char [] anyOf)
865 if (anyOf == null)
866 throw new ArgumentNullException ();
867 if (this.length == 0)
868 return -1;
870 return IndexOfAnyUnchecked (anyOf, 0, this.length);
873 public int IndexOfAny (char [] anyOf, int startIndex)
875 if (anyOf == null)
876 throw new ArgumentNullException ();
877 if (startIndex < 0 || startIndex > this.length)
878 throw new ArgumentOutOfRangeException ();
880 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
883 public int IndexOfAny (char [] anyOf, int startIndex, int count)
885 if (anyOf == null)
886 throw new ArgumentNullException ();
887 if (startIndex < 0 || startIndex > this.length)
888 throw new ArgumentOutOfRangeException ();
889 if (count < 0 || startIndex > this.length - count)
890 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
892 return IndexOfAnyUnchecked (anyOf, startIndex, count);
895 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
897 if (anyOf.Length == 0)
898 return -1;
900 if (anyOf.Length == 1)
901 return IndexOfUnchecked (anyOf[0], startIndex, count);
903 fixed (char* any = anyOf) {
904 int highest = *any;
905 int lowest = *any;
907 char* end_any_ptr = any + anyOf.Length;
908 char* any_ptr = any;
909 while (++any_ptr != end_any_ptr) {
910 if (*any_ptr > highest) {
911 highest = *any_ptr;
912 continue;
915 if (*any_ptr < lowest)
916 lowest = *any_ptr;
919 fixed (char* start = &start_char) {
920 char* ptr = start + startIndex;
921 char* end_ptr = ptr + count;
923 while (ptr != end_ptr) {
924 if (*ptr > highest || *ptr < lowest) {
925 ptr++;
926 continue;
929 if (*ptr == *any)
930 return (int)(ptr - start);
932 any_ptr = any;
933 while (++any_ptr != end_any_ptr) {
934 if (*ptr == *any_ptr)
935 return (int)(ptr - start);
938 ptr++;
942 return -1;
946 public int IndexOf (string value, StringComparison comparisonType)
948 return IndexOf (value, 0, this.Length, comparisonType);
951 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
953 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
956 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
958 switch (comparisonType) {
959 case StringComparison.CurrentCulture:
960 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
961 case StringComparison.CurrentCultureIgnoreCase:
962 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
963 case StringComparison.InvariantCulture:
964 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
965 case StringComparison.InvariantCultureIgnoreCase:
966 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
967 case StringComparison.Ordinal:
968 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
969 case StringComparison.OrdinalIgnoreCase:
970 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
971 default:
972 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
973 throw new ArgumentException (msg, "comparisonType");
977 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
979 if (value == null)
980 throw new ArgumentNullException ("value");
981 if (startIndex < 0)
982 throw new ArgumentOutOfRangeException ("startIndex");
983 if (count < 0 || (this.length - startIndex) < count)
984 throw new ArgumentOutOfRangeException ("count");
986 if (options == CompareOptions.Ordinal)
987 return IndexOfOrdinalUnchecked (value, startIndex, count);
988 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
991 internal unsafe int IndexOfOrdinalUnchecked (string value)
993 return IndexOfOrdinalUnchecked (value, 0, length);
996 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
998 int valueLen = value.Length;
999 if (count < valueLen)
1000 return -1;
1002 if (valueLen <= 1) {
1003 if (valueLen == 1)
1004 return IndexOfUnchecked (value[0], startIndex, count);
1005 return startIndex;
1008 fixed (char* thisptr = this, valueptr = value) {
1009 char* ap = thisptr + startIndex;
1010 char* thisEnd = ap + count - valueLen + 1;
1011 while (ap != thisEnd) {
1012 if (*ap == *valueptr) {
1013 for (int i = 1; i < valueLen; i++) {
1014 if (ap[i] != valueptr[i])
1015 goto NextVal;
1017 return (int)(ap - thisptr);
1019 NextVal:
1020 ap++;
1023 return -1;
1026 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1028 int valueLen = value.Length;
1029 if (count < valueLen)
1030 return -1;
1032 if (valueLen == 0)
1033 return startIndex;
1035 fixed (char* thisptr = this, valueptr = value) {
1036 char* ap = thisptr + startIndex;
1037 char* thisEnd = ap + count - valueLen + 1;
1038 while (ap != thisEnd) {
1039 for (int i = 0; i < valueLen; i++) {
1040 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1041 goto NextVal;
1043 return (int)(ap - thisptr);
1044 NextVal:
1045 ap++;
1048 return -1;
1051 public int LastIndexOf (string value, StringComparison comparisonType)
1053 if (this.Length == 0)
1054 return value.Length == 0 ? 0 : -1;
1055 else
1056 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1059 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1061 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1064 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1066 switch (comparisonType) {
1067 case StringComparison.CurrentCulture:
1068 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1069 case StringComparison.CurrentCultureIgnoreCase:
1070 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1071 case StringComparison.InvariantCulture:
1072 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1073 case StringComparison.InvariantCultureIgnoreCase:
1074 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1075 case StringComparison.Ordinal:
1076 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1077 case StringComparison.OrdinalIgnoreCase:
1078 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1079 default:
1080 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1081 throw new ArgumentException (msg, "comparisonType");
1085 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1087 if (value == null)
1088 throw new ArgumentNullException ("value");
1089 if (this.Length == 0)
1090 return value.Length == 0 ? 0 : -1;
1091 if (value.Length == 0)
1092 return Math.Min (this.Length - 1, startIndex);
1093 if (startIndex < 0 || startIndex > length)
1094 throw new ArgumentOutOfRangeException ("startIndex");
1095 if (count < 0 || (startIndex < count - 1))
1096 throw new ArgumentOutOfRangeException ("count");
1098 if (options == CompareOptions.Ordinal)
1099 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1100 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1103 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1105 int valueLen = value.Length;
1106 if (count < valueLen)
1107 return -1;
1109 if (valueLen <= 1) {
1110 if (valueLen == 1)
1111 return LastIndexOfUnchecked (value[0], startIndex, count);
1112 return startIndex;
1115 fixed (char* thisptr = this, valueptr = value) {
1116 char* ap = thisptr + startIndex - valueLen + 1;
1117 char* thisEnd = ap - count + valueLen - 1;
1118 while (ap != thisEnd) {
1119 if (*ap == *valueptr) {
1120 for (int i = 1; i < valueLen; i++) {
1121 if (ap[i] != valueptr[i])
1122 goto NextVal;
1124 return (int)(ap - thisptr);
1126 NextVal:
1127 ap--;
1130 return -1;
1133 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1135 int valueLen = value.Length;
1136 if (count < valueLen)
1137 return -1;
1139 if (valueLen == 0)
1140 return startIndex;
1142 fixed (char* thisptr = this, valueptr = value) {
1143 char* ap = thisptr + startIndex - valueLen + 1;
1144 char* thisEnd = ap - count + valueLen - 1;
1145 while (ap != thisEnd) {
1146 for (int i = 0; i < valueLen; i++) {
1147 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1148 goto NextVal;
1150 return (int)(ap - thisptr);
1151 NextVal:
1152 ap--;
1155 return -1;
1158 // Following methods are culture-insensitive
1159 public int IndexOf (char value)
1161 if (this.length == 0)
1162 return -1;
1164 return IndexOfUnchecked (value, 0, this.length);
1167 public int IndexOf (char value, int startIndex)
1169 if (startIndex < 0)
1170 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1171 if (startIndex > this.length)
1172 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1174 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1175 return -1;
1177 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1180 public int IndexOf (char value, int startIndex, int count)
1182 if (startIndex < 0 || startIndex > this.length)
1183 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1184 if (count < 0)
1185 throw new ArgumentOutOfRangeException ("count", "< 0");
1186 if (startIndex > this.length - count)
1187 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1189 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1190 return -1;
1192 return IndexOfUnchecked (value, startIndex, count);
1195 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1197 // It helps JIT compiler to optimize comparison
1198 int value_32 = (int)value;
1200 fixed (char* start = &start_char) {
1201 char* ptr = start + startIndex;
1202 char* end_ptr = ptr + (count >> 3 << 3);
1204 while (ptr != end_ptr) {
1205 if (*ptr == value_32)
1206 return (int)(ptr - start);
1207 if (ptr[1] == value_32)
1208 return (int)(ptr - start + 1);
1209 if (ptr[2] == value_32)
1210 return (int)(ptr - start + 2);
1211 if (ptr[3] == value_32)
1212 return (int)(ptr - start + 3);
1213 if (ptr[4] == value_32)
1214 return (int)(ptr - start + 4);
1215 if (ptr[5] == value_32)
1216 return (int)(ptr - start + 5);
1217 if (ptr[6] == value_32)
1218 return (int)(ptr - start + 6);
1219 if (ptr[7] == value_32)
1220 return (int)(ptr - start + 7);
1222 ptr += 8;
1225 end_ptr += count & 0x07;
1226 while (ptr != end_ptr) {
1227 if (*ptr == value_32)
1228 return (int)(ptr - start);
1230 ptr++;
1232 return -1;
1236 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1238 if (length == 0)
1239 return -1;
1240 int end = startIndex + count;
1241 char c = Char.ToUpperInvariant (value);
1242 fixed (char* s = &start_char) {
1243 for (int i = startIndex; i < end; i++)
1244 if (Char.ToUpperInvariant (s [i]) == c)
1245 return i;
1247 return -1;
1250 // Following methods are culture-sensitive
1251 public int IndexOf (String value)
1253 if (value == null)
1254 throw new ArgumentNullException ("value");
1255 if (value.Length == 0)
1256 return 0;
1257 if (this.length == 0)
1258 return -1;
1259 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.None);
1262 public int IndexOf (String value, int startIndex)
1264 return IndexOf (value, startIndex, this.length - startIndex);
1267 public int IndexOf (String value, int startIndex, int count)
1269 if (value == null)
1270 throw new ArgumentNullException ("value");
1271 if (startIndex < 0 || startIndex > this.length)
1272 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1273 if (count < 0 || startIndex > this.length - count)
1274 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1276 if (value.length == 0)
1277 return startIndex;
1279 if (startIndex == 0 && this.length == 0)
1280 return -1;
1282 if (count == 0)
1283 return -1;
1285 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1288 // Following methods are culture-insensitive
1289 public int LastIndexOfAny (char [] anyOf)
1291 if (anyOf == null)
1292 throw new ArgumentNullException ();
1293 if (this.length == 0)
1294 return -1;
1296 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1299 public int LastIndexOfAny (char [] anyOf, int startIndex)
1301 if (anyOf == null)
1302 throw new ArgumentNullException ();
1303 if (this.length == 0)
1304 return -1;
1306 if (startIndex < 0 || startIndex >= this.length)
1307 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1309 if (this.length == 0)
1310 return -1;
1312 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1315 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1317 if (anyOf == null)
1318 throw new ArgumentNullException ();
1319 if (this.length == 0)
1320 return -1;
1322 if ((startIndex < 0) || (startIndex >= this.Length))
1323 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1324 if ((count < 0) || (count > this.Length))
1325 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1326 if (startIndex - count + 1 < 0)
1327 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1329 if (this.length == 0)
1330 return -1;
1332 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1335 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1337 if (anyOf.Length == 1)
1338 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1340 fixed (char* start = this, testStart = anyOf) {
1341 char* ptr = start + startIndex;
1342 char* ptrEnd = ptr - count;
1343 char* test;
1344 char* testEnd = testStart + anyOf.Length;
1346 while (ptr != ptrEnd) {
1347 test = testStart;
1348 while (test != testEnd) {
1349 if (*test == *ptr)
1350 return (int)(ptr - start);
1351 test++;
1353 ptr--;
1355 return -1;
1359 // Following methods are culture-insensitive
1360 public int LastIndexOf (char value)
1362 if (this.length == 0)
1363 return -1;
1365 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1368 public int LastIndexOf (char value, int startIndex)
1370 return LastIndexOf (value, startIndex, startIndex + 1);
1373 public int LastIndexOf (char value, int startIndex, int count)
1375 if (this.length == 0)
1376 return -1;
1378 // >= for char (> for string)
1379 if ((startIndex < 0) || (startIndex >= this.Length))
1380 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1381 if ((count < 0) || (count > this.Length))
1382 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1383 if (startIndex - count + 1 < 0)
1384 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1386 return LastIndexOfUnchecked (value, startIndex, count);
1389 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1391 // It helps JIT compiler to optimize comparison
1392 int value_32 = (int)value;
1394 fixed (char* start = &start_char) {
1395 char* ptr = start + startIndex;
1396 char* end_ptr = ptr - (count >> 3 << 3);
1398 while (ptr != end_ptr) {
1399 if (*ptr == value_32)
1400 return (int)(ptr - start);
1401 if (ptr[-1] == value_32)
1402 return (int)(ptr - start) - 1;
1403 if (ptr[-2] == value_32)
1404 return (int)(ptr - start) - 2;
1405 if (ptr[-3] == value_32)
1406 return (int)(ptr - start) - 3;
1407 if (ptr[-4] == value_32)
1408 return (int)(ptr - start) - 4;
1409 if (ptr[-5] == value_32)
1410 return (int)(ptr - start) - 5;
1411 if (ptr[-6] == value_32)
1412 return (int)(ptr - start) - 6;
1413 if (ptr[-7] == value_32)
1414 return (int)(ptr - start) - 7;
1416 ptr -= 8;
1419 end_ptr -= count & 0x07;
1420 while (ptr != end_ptr) {
1421 if (*ptr == value_32)
1422 return (int)(ptr - start);
1424 ptr--;
1426 return -1;
1430 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1432 if (length == 0)
1433 return -1;
1434 int end = startIndex - count;
1435 char c = Char.ToUpperInvariant (value);
1436 fixed (char* s = &start_char) {
1437 for (int i = startIndex; i > end; i--)
1438 if (Char.ToUpperInvariant (s [i]) == c)
1439 return i;
1441 return -1;
1444 // Following methods are culture-sensitive
1445 public int LastIndexOf (String value)
1447 return LastIndexOf (value, this.length - 1, this.length);
1450 public int LastIndexOf (String value, int startIndex)
1452 int max = startIndex;
1453 if (max < this.Length)
1454 max++;
1455 return LastIndexOf (value, startIndex, max);
1458 public int LastIndexOf (String value, int startIndex, int count)
1460 if (value == null)
1461 throw new ArgumentNullException ("value");
1463 if (this.length == 0)
1464 return value.Length == 0 ? 0 : -1;
1465 // -1 > startIndex > for string (0 > startIndex >= for char)
1466 if ((startIndex < -1) || (startIndex > this.Length))
1467 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1468 if ((count < 0) || (count > this.Length))
1469 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1470 if (startIndex - count + 1 < 0)
1471 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1473 if (value.Length == 0)
1474 return Math.Min (this.Length - 1, startIndex);
1476 if (startIndex == 0 && this.length == 0)
1477 return -1;
1479 // This check is needed to match undocumented MS behaviour
1480 if (this.length == 0 && value.length > 0)
1481 return -1;
1483 if (count == 0)
1484 return -1;
1486 if (startIndex == this.Length)
1487 startIndex--;
1488 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1491 public bool Contains (String value)
1493 if (value == null)
1494 throw new ArgumentNullException ("value");
1496 return IndexOfOrdinalUnchecked (value, 0, Length) != -1;
1499 public static bool IsNullOrEmpty (String value)
1501 return (value == null) || (value.Length == 0);
1504 public string Normalize ()
1506 return Normalization.Normalize (this, 0);
1509 public string Normalize (NormalizationForm normalizationForm)
1511 switch (normalizationForm) {
1512 default:
1513 return Normalization.Normalize (this, 0);
1514 case NormalizationForm.FormD:
1515 return Normalization.Normalize (this, 1);
1516 case NormalizationForm.FormKC:
1517 return Normalization.Normalize (this, 2);
1518 case NormalizationForm.FormKD:
1519 return Normalization.Normalize (this, 3);
1523 public bool IsNormalized ()
1525 return Normalization.IsNormalized (this, 0);
1528 public bool IsNormalized (NormalizationForm normalizationForm)
1530 switch (normalizationForm) {
1531 default:
1532 return Normalization.IsNormalized (this, 0);
1533 case NormalizationForm.FormD:
1534 return Normalization.IsNormalized (this, 1);
1535 case NormalizationForm.FormKC:
1536 return Normalization.IsNormalized (this, 2);
1537 case NormalizationForm.FormKD:
1538 return Normalization.IsNormalized (this, 3);
1542 public string Remove (int startIndex)
1544 if (startIndex < 0)
1545 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1546 if (startIndex >= this.length)
1547 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1549 return Remove (startIndex, this.length - startIndex);
1552 public String PadLeft (int totalWidth)
1554 return PadLeft (totalWidth, ' ');
1557 public unsafe String PadLeft (int totalWidth, char paddingChar)
1559 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1561 if (totalWidth < 0)
1562 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1564 if (totalWidth < this.length)
1565 return this;
1566 if (totalWidth == 0)
1567 return Empty;
1569 String tmp = InternalAllocateStr (totalWidth);
1571 fixed (char* dest = tmp, src = this) {
1572 char* padPos = dest;
1573 char* padTo;
1574 try {
1575 padTo = checked (dest + (totalWidth - length));
1576 } catch (OverflowException) {
1577 throw new OutOfMemoryException ();
1580 while (padPos != padTo)
1581 *padPos++ = paddingChar;
1583 CharCopy (padTo, src, length);
1585 return tmp;
1588 public String PadRight (int totalWidth)
1590 return PadRight (totalWidth, ' ');
1593 public unsafe String PadRight (int totalWidth, char paddingChar)
1595 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1597 if (totalWidth < 0)
1598 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1600 if (totalWidth < this.length)
1601 return this;
1602 if (totalWidth == 0)
1603 return Empty;
1605 String tmp = InternalAllocateStr (totalWidth);
1607 fixed (char* dest = tmp, src = this) {
1608 CharCopy (dest, src, length);
1610 try {
1611 char* padPos = checked (dest + length);
1612 char* padTo = checked (dest + totalWidth);
1613 while (padPos != padTo)
1614 *padPos++ = paddingChar;
1615 } catch (OverflowException) {
1616 throw new OutOfMemoryException ();
1619 return tmp;
1622 public bool StartsWith (String value)
1624 if (value == null)
1625 throw new ArgumentNullException ("value");
1627 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1630 [ComVisible (false)]
1631 public bool StartsWith (string value, StringComparison comparisonType)
1633 if (value == null)
1634 throw new ArgumentNullException ("value");
1636 switch (comparisonType) {
1637 case StringComparison.CurrentCulture:
1638 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1639 case StringComparison.CurrentCultureIgnoreCase:
1640 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1641 case StringComparison.InvariantCulture:
1642 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1643 case StringComparison.InvariantCultureIgnoreCase:
1644 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1645 case StringComparison.Ordinal:
1646 return StartsWithOrdinalUnchecked (value);
1647 case StringComparison.OrdinalIgnoreCase:
1648 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1649 default:
1650 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1651 throw new ArgumentException (msg, "comparisonType");
1655 internal bool StartsWithOrdinalUnchecked (string value)
1657 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1660 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1662 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1665 [ComVisible (false)]
1666 public bool EndsWith (string value, StringComparison comparisonType)
1668 if (value == null)
1669 throw new ArgumentNullException ("value");
1671 switch (comparisonType) {
1672 case StringComparison.CurrentCulture:
1673 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1674 case StringComparison.CurrentCultureIgnoreCase:
1675 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1676 case StringComparison.InvariantCulture:
1677 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1678 case StringComparison.InvariantCultureIgnoreCase:
1679 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1680 case StringComparison.Ordinal:
1681 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1682 case StringComparison.OrdinalIgnoreCase:
1683 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1684 default:
1685 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1686 throw new ArgumentException (msg, "comparisonType");
1690 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1692 if (culture == null)
1693 culture = CultureInfo.CurrentCulture;
1695 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1698 // Following method is culture-insensitive
1699 public unsafe String Replace (char oldChar, char newChar)
1701 if (this.length == 0 || oldChar == newChar)
1702 return this;
1704 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1705 if (start_pos == -1)
1706 return this;
1708 if (start_pos < 4)
1709 start_pos = 0;
1711 string tmp = InternalAllocateStr (length);
1712 fixed (char* dest = tmp, src = &start_char) {
1713 if (start_pos != 0)
1714 CharCopy (dest, src, start_pos);
1716 char* end_ptr = dest + length;
1717 char* dest_ptr = dest + start_pos;
1718 char* src_ptr = src + start_pos;
1720 while (dest_ptr != end_ptr) {
1721 if (*src_ptr == oldChar)
1722 *dest_ptr = newChar;
1723 else
1724 *dest_ptr = *src_ptr;
1726 ++src_ptr;
1727 ++dest_ptr;
1730 return tmp;
1733 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1734 public String Replace (String oldValue, String newValue)
1736 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1737 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1739 if (oldValue == null)
1740 throw new ArgumentNullException ("oldValue");
1742 if (oldValue.Length == 0)
1743 throw new ArgumentException ("oldValue is the empty string.");
1745 if (this.Length == 0)
1746 return this;
1748 if (newValue == null)
1749 newValue = Empty;
1751 return ReplaceUnchecked (oldValue, newValue);
1754 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1756 if (oldValue.length > length)
1757 return this;
1758 if (oldValue.length == 1 && newValue.length == 1) {
1759 return Replace (oldValue[0], newValue[0]);
1760 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1761 // because the length of the result would be this.length and length calculation unneccesary
1764 const int maxValue = 200; // Allocate 800 byte maximum
1765 int* dat = stackalloc int[maxValue];
1766 fixed (char* source = this, replace = newValue) {
1767 int i = 0, count = 0;
1768 while (i < length) {
1769 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1770 if (found < 0)
1771 break;
1772 else {
1773 if (count < maxValue)
1774 dat[count++] = found;
1775 else
1776 return ReplaceFallback (oldValue, newValue, maxValue);
1778 i = found + oldValue.length;
1780 if (count == 0)
1781 return this;
1782 int nlen = 0;
1783 checked {
1784 try {
1785 nlen = this.length + ((newValue.length - oldValue.length) * count);
1786 } catch (OverflowException) {
1787 throw new OutOfMemoryException ();
1790 String tmp = InternalAllocateStr (nlen);
1792 int curPos = 0, lastReadPos = 0;
1793 fixed (char* dest = tmp) {
1794 for (int j = 0; j < count; j++) {
1795 int precopy = dat[j] - lastReadPos;
1796 CharCopy (dest + curPos, source + lastReadPos, precopy);
1797 curPos += precopy;
1798 lastReadPos = dat[j] + oldValue.length;
1799 CharCopy (dest + curPos, replace, newValue.length);
1800 curPos += newValue.length;
1802 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1804 return tmp;
1808 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1810 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1811 StringBuilder sb = new StringBuilder (lengthEstimate);
1812 for (int i = 0; i < length;) {
1813 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1814 if (found < 0) {
1815 sb.Append (SubstringUnchecked (i, length - i));
1816 break;
1818 sb.Append (SubstringUnchecked (i, found - i));
1819 sb.Append (newValue);
1820 i = found + oldValue.Length;
1822 return sb.ToString ();
1826 public unsafe String Remove (int startIndex, int count)
1828 if (startIndex < 0)
1829 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1830 if (count < 0)
1831 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1832 if (startIndex > this.length - count)
1833 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1835 String tmp = InternalAllocateStr (this.length - count);
1837 fixed (char *dest = tmp, src = this) {
1838 char *dst = dest;
1839 CharCopy (dst, src, startIndex);
1840 int skip = startIndex + count;
1841 dst += startIndex;
1842 CharCopy (dst, src + skip, length - skip);
1844 return tmp;
1847 public String ToLower ()
1849 return ToLower (CultureInfo.CurrentCulture);
1852 public String ToLower (CultureInfo culture)
1854 if (culture == null)
1855 throw new ArgumentNullException ("culture");
1857 if (culture.LCID == 0x007F) // Invariant
1858 return ToLowerInvariant ();
1860 return culture.TextInfo.ToLower (this);
1863 public unsafe String ToLowerInvariant ()
1865 if (length == 0)
1866 return Empty;
1868 string tmp = InternalAllocateStr (length);
1869 fixed (char* source = &start_char, dest = tmp) {
1871 char* destPtr = (char*)dest;
1872 char* sourcePtr = (char*)source;
1874 for (int n = 0; n < length; n++) {
1875 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1876 sourcePtr++;
1877 destPtr++;
1880 return tmp;
1883 public String ToUpper ()
1885 return ToUpper (CultureInfo.CurrentCulture);
1888 public String ToUpper (CultureInfo culture)
1890 if (culture == null)
1891 throw new ArgumentNullException ("culture");
1893 if (culture.LCID == 0x007F) // Invariant
1894 return ToUpperInvariant ();
1896 return culture.TextInfo.ToUpper (this);
1899 public unsafe String ToUpperInvariant ()
1901 if (length == 0)
1902 return Empty;
1904 string tmp = InternalAllocateStr (length);
1905 fixed (char* source = &start_char, dest = tmp) {
1907 char* destPtr = (char*)dest;
1908 char* sourcePtr = (char*)source;
1910 for (int n = 0; n < length; n++) {
1911 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1912 sourcePtr++;
1913 destPtr++;
1916 return tmp;
1919 public override String ToString ()
1921 return this;
1924 public String ToString (IFormatProvider provider)
1926 return this;
1929 public static String Format (String format, Object arg0)
1931 return Format (null, format, new Object[] {arg0});
1934 public static String Format (String format, Object arg0, Object arg1)
1936 return Format (null, format, new Object[] {arg0, arg1});
1939 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1941 return Format (null, format, new Object[] {arg0, arg1, arg2});
1944 public static string Format (string format, params object[] args)
1946 return Format (null, format, args);
1949 public static string Format (IFormatProvider provider, string format, params object[] args)
1951 StringBuilder b = FormatHelper (null, provider, format, args);
1952 return b.ToString ();
1955 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1957 if (format == null)
1958 throw new ArgumentNullException ("format");
1959 if (args == null)
1960 throw new ArgumentNullException ("args");
1962 if (result == null) {
1963 /* Try to approximate the size of result to avoid reallocations */
1964 int i, len;
1966 len = 0;
1967 for (i = 0; i < args.Length; ++i) {
1968 string s = args [i] as string;
1969 if (s != null)
1970 len += s.length;
1971 else
1972 break;
1974 if (i == args.Length)
1975 result = new StringBuilder (len + format.length);
1976 else
1977 result = new StringBuilder ();
1980 int ptr = 0;
1981 int start = ptr;
1982 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1984 while (ptr < format.length) {
1985 char c = format[ptr ++];
1987 if (c == '{') {
1988 result.Append (format, start, ptr - start - 1);
1990 // check for escaped open bracket
1992 if (format[ptr] == '{') {
1993 start = ptr ++;
1994 continue;
1997 // parse specifier
1999 int n, width;
2000 bool left_align;
2001 string arg_format;
2003 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2004 if (n >= args.Length)
2005 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2007 // format argument
2009 object arg = args[n];
2011 string str;
2012 if (arg == null)
2013 str = Empty;
2014 else if (formatter != null)
2015 str = formatter.Format (arg_format, arg, provider);
2016 else
2017 str = null;
2019 if (str == null) {
2020 if (arg is IFormattable)
2021 str = ((IFormattable)arg).ToString (arg_format, provider);
2022 else
2023 str = arg.ToString () ?? Empty;
2026 // pad formatted string and append to result
2027 if (width > str.length) {
2028 const char padchar = ' ';
2029 int padlen = width - str.length;
2031 if (left_align) {
2032 result.Append (str);
2033 result.Append (padchar, padlen);
2035 else {
2036 result.Append (padchar, padlen);
2037 result.Append (str);
2039 } else {
2040 result.Append (str);
2043 start = ptr;
2045 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2046 result.Append (format, start, ptr - start - 1);
2047 start = ptr ++;
2049 else if (c == '}') {
2050 throw new FormatException ("Input string was not in a correct format.");
2054 if (start < format.length)
2055 result.Append (format, start, format.Length - start);
2057 return result;
2060 public unsafe static String Copy (String str)
2062 if (str == null)
2063 throw new ArgumentNullException ("str");
2065 int length = str.length;
2067 String tmp = InternalAllocateStr (length);
2068 if (length != 0) {
2069 fixed (char *dest = tmp, src = str) {
2070 CharCopy (dest, src, length);
2073 return tmp;
2076 public static String Concat (Object arg0)
2078 if (arg0 == null)
2079 return Empty;
2081 return arg0.ToString ();
2084 public static String Concat (Object arg0, Object arg1)
2086 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2089 public static String Concat (Object arg0, Object arg1, Object arg2)
2091 string s1, s2, s3;
2092 if (arg0 == null)
2093 s1 = Empty;
2094 else
2095 s1 = arg0.ToString ();
2097 if (arg1 == null)
2098 s2 = Empty;
2099 else
2100 s2 = arg1.ToString ();
2102 if (arg2 == null)
2103 s3 = Empty;
2104 else
2105 s3 = arg2.ToString ();
2107 return Concat (s1, s2, s3);
2110 [CLSCompliant(false)]
2111 public static String Concat (Object arg0, Object arg1, Object arg2,
2112 Object arg3, __arglist)
2114 string s1, s2, s3, s4;
2116 if (arg0 == null)
2117 s1 = Empty;
2118 else
2119 s1 = arg0.ToString ();
2121 if (arg1 == null)
2122 s2 = Empty;
2123 else
2124 s2 = arg1.ToString ();
2126 if (arg2 == null)
2127 s3 = Empty;
2128 else
2129 s3 = arg2.ToString ();
2131 ArgIterator iter = new ArgIterator (__arglist);
2132 int argCount = iter.GetRemainingCount();
2134 StringBuilder sb = new StringBuilder ();
2135 if (arg3 != null)
2136 sb.Append (arg3.ToString ());
2138 for (int i = 0; i < argCount; i++) {
2139 TypedReference typedRef = iter.GetNextArg ();
2140 sb.Append (TypedReference.ToObject (typedRef));
2143 s4 = sb.ToString ();
2145 return Concat (s1, s2, s3, s4);
2148 public unsafe static String Concat (String str0, String str1)
2150 if (str0 == null || str0.Length == 0) {
2151 if (str1 == null || str1.Length == 0)
2152 return Empty;
2153 return str1;
2156 if (str1 == null || str1.Length == 0)
2157 return str0;
2159 int nlen = str0.length + str1.length;
2160 if (nlen < 0)
2161 throw new OutOfMemoryException ();
2162 String tmp = InternalAllocateStr (nlen);
2164 fixed (char *dest = tmp, src = str0)
2165 CharCopy (dest, src, str0.length);
2166 fixed (char *dest = tmp, src = str1)
2167 CharCopy (dest + str0.Length, src, str1.length);
2169 return tmp;
2172 public unsafe static String Concat (String str0, String str1, String str2)
2174 if (str0 == null || str0.Length == 0){
2175 if (str1 == null || str1.Length == 0){
2176 if (str2 == null || str2.Length == 0)
2177 return Empty;
2178 return str2;
2179 } else {
2180 if (str2 == null || str2.Length == 0)
2181 return str1;
2183 str0 = Empty;
2184 } else {
2185 if (str1 == null || str1.Length == 0){
2186 if (str2 == null || str2.Length == 0)
2187 return str0;
2188 else
2189 str1 = Empty;
2190 } else {
2191 if (str2 == null || str2.Length == 0)
2192 str2 = Empty;
2196 int nlen = str0.length + str1.length;
2197 if (nlen < 0)
2198 throw new OutOfMemoryException ();
2199 nlen += str2.length;
2200 if (nlen < 0)
2201 throw new OutOfMemoryException ();
2202 String tmp = InternalAllocateStr (nlen);
2204 if (str0.Length != 0) {
2205 fixed (char *dest = tmp, src = str0) {
2206 CharCopy (dest, src, str0.length);
2209 if (str1.Length != 0) {
2210 fixed (char *dest = tmp, src = str1) {
2211 CharCopy (dest + str0.Length, src, str1.length);
2214 if (str2.Length != 0) {
2215 fixed (char *dest = tmp, src = str2) {
2216 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2220 return tmp;
2223 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2225 if (str0 == null && str1 == null && str2 == null && str3 == null)
2226 return Empty;
2228 if (str0 == null)
2229 str0 = Empty;
2230 if (str1 == null)
2231 str1 = Empty;
2232 if (str2 == null)
2233 str2 = Empty;
2234 if (str3 == null)
2235 str3 = Empty;
2237 int nlen = str0.length + str1.length;
2238 if (nlen < 0)
2239 throw new OutOfMemoryException ();
2240 nlen += str2.length;
2241 if (nlen < 0)
2242 throw new OutOfMemoryException ();
2243 nlen += str3.length;
2244 if (nlen < 0)
2245 throw new OutOfMemoryException ();
2246 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2248 if (str0.Length != 0) {
2249 fixed (char *dest = tmp, src = str0) {
2250 CharCopy (dest, src, str0.length);
2253 if (str1.Length != 0) {
2254 fixed (char *dest = tmp, src = str1) {
2255 CharCopy (dest + str0.Length, src, str1.length);
2258 if (str2.Length != 0) {
2259 fixed (char *dest = tmp, src = str2) {
2260 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2263 if (str3.Length != 0) {
2264 fixed (char *dest = tmp, src = str3) {
2265 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2269 return tmp;
2272 public static String Concat (params Object[] args)
2274 if (args == null)
2275 throw new ArgumentNullException ("args");
2277 int argLen = args.Length;
2278 if (argLen == 0)
2279 return Empty;
2281 string [] strings = new string [argLen];
2282 int len = 0;
2283 for (int i = 0; i < argLen; i++) {
2284 if (args[i] != null) {
2285 strings[i] = args[i].ToString ();
2286 len += strings[i].length;
2287 if (len < 0)
2288 throw new OutOfMemoryException ();
2292 return ConcatInternal (strings, len);
2295 public static String Concat (params String[] values)
2297 if (values == null)
2298 throw new ArgumentNullException ("values");
2300 int len = 0;
2301 for (int i = 0; i < values.Length; i++) {
2302 String s = values[i];
2303 if (s != null)
2304 len += s.length;
2305 if (len < 0)
2306 throw new OutOfMemoryException ();
2309 return ConcatInternal (values, len);
2312 private static unsafe String ConcatInternal (String[] values, int length)
2314 if (length == 0)
2315 return Empty;
2316 if (length < 0)
2317 throw new OutOfMemoryException ();
2319 String tmp = InternalAllocateStr (length);
2321 fixed (char* dest = tmp) {
2322 int pos = 0;
2323 for (int i = 0; i < values.Length; i++) {
2324 String source = values[i];
2325 if (source != null) {
2326 fixed (char* src = source) {
2327 CharCopy (dest + pos, src, source.length);
2329 pos += source.Length;
2333 return tmp;
2336 public unsafe String Insert (int startIndex, String value)
2338 if (value == null)
2339 throw new ArgumentNullException ("value");
2341 if (startIndex < 0 || startIndex > this.length)
2342 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2344 if (value.Length == 0)
2345 return this;
2346 if (this.Length == 0)
2347 return value;
2349 int nlen = this.length + value.length;
2350 if (nlen < 0)
2351 throw new OutOfMemoryException ();
2353 String tmp = InternalAllocateStr (nlen);
2355 fixed (char *dest = tmp, src = this, val = value) {
2356 char *dst = dest;
2357 CharCopy (dst, src, startIndex);
2358 dst += startIndex;
2359 CharCopy (dst, val, value.length);
2360 dst += value.length;
2361 CharCopy (dst, src + startIndex, length - startIndex);
2363 return tmp;
2366 public static string Intern (string str)
2368 if (str == null)
2369 throw new ArgumentNullException ("str");
2371 return InternalIntern (str);
2374 public static string IsInterned (string str)
2376 if (str == null)
2377 throw new ArgumentNullException ("str");
2379 return InternalIsInterned (str);
2382 public static string Join (string separator, params string [] value)
2384 if (value == null)
2385 throw new ArgumentNullException ("value");
2386 if (separator == null)
2387 separator = Empty;
2389 return JoinUnchecked (separator, value, 0, value.Length);
2392 public static string Join (string separator, string[] value, int startIndex, int count)
2394 if (value == null)
2395 throw new ArgumentNullException ("value");
2396 if (startIndex < 0)
2397 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2398 if (count < 0)
2399 throw new ArgumentOutOfRangeException ("count", "< 0");
2400 if (startIndex > value.Length - count)
2401 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2403 if (startIndex == value.Length)
2404 return Empty;
2405 if (separator == null)
2406 separator = Empty;
2408 return JoinUnchecked (separator, value, startIndex, count);
2411 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2413 // Unchecked parameters
2414 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2415 // separator and value must not be null
2417 int length = 0;
2418 int maxIndex = startIndex + count;
2419 // Precount the number of characters that the resulting string will have
2420 for (int i = startIndex; i < maxIndex; i++) {
2421 String s = value[i];
2422 if (s != null)
2423 length += s.length;
2425 length += separator.length * (count - 1);
2426 if (length <= 0)
2427 return Empty;
2429 String tmp = InternalAllocateStr (length);
2431 maxIndex--;
2432 fixed (char* dest = tmp, sepsrc = separator) {
2433 // Copy each string from value except the last one and add a separator for each
2434 int pos = 0;
2435 for (int i = startIndex; i < maxIndex; i++) {
2436 String source = value[i];
2437 if (source != null) {
2438 if (source.Length > 0) {
2439 fixed (char* src = source)
2440 CharCopy (dest + pos, src, source.Length);
2441 pos += source.Length;
2444 if (separator.Length > 0) {
2445 CharCopy (dest + pos, sepsrc, separator.Length);
2446 pos += separator.Length;
2449 // Append last string that does not get an additional separator
2450 String sourceLast = value[maxIndex];
2451 if (sourceLast != null) {
2452 if (sourceLast.Length > 0) {
2453 fixed (char* src = sourceLast)
2454 CharCopy (dest + pos, src, sourceLast.Length);
2458 return tmp;
2461 bool IConvertible.ToBoolean (IFormatProvider provider)
2463 return Convert.ToBoolean (this, provider);
2466 byte IConvertible.ToByte (IFormatProvider provider)
2468 return Convert.ToByte (this, provider);
2471 char IConvertible.ToChar (IFormatProvider provider)
2473 return Convert.ToChar (this, provider);
2476 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2478 return Convert.ToDateTime (this, provider);
2481 decimal IConvertible.ToDecimal (IFormatProvider provider)
2483 return Convert.ToDecimal (this, provider);
2486 double IConvertible.ToDouble (IFormatProvider provider)
2488 return Convert.ToDouble (this, provider);
2491 short IConvertible.ToInt16 (IFormatProvider provider)
2493 return Convert.ToInt16 (this, provider);
2496 int IConvertible.ToInt32 (IFormatProvider provider)
2498 return Convert.ToInt32 (this, provider);
2501 long IConvertible.ToInt64 (IFormatProvider provider)
2503 return Convert.ToInt64 (this, provider);
2506 sbyte IConvertible.ToSByte (IFormatProvider provider)
2508 return Convert.ToSByte (this, provider);
2511 float IConvertible.ToSingle (IFormatProvider provider)
2513 return Convert.ToSingle (this, provider);
2516 object IConvertible.ToType (Type type, IFormatProvider provider)
2518 return Convert.DefaultToType ((IConvertible)this, type, provider);
2521 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2523 return Convert.ToUInt16 (this, provider);
2526 uint IConvertible.ToUInt32 (IFormatProvider provider)
2528 return Convert.ToUInt32 (this, provider);
2531 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2533 return Convert.ToUInt64 (this, provider);
2536 public int Length {
2537 get {
2538 return length;
2542 public CharEnumerator GetEnumerator ()
2544 return new CharEnumerator (this);
2547 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2549 return new CharEnumerator (this);
2552 IEnumerator IEnumerable.GetEnumerator ()
2554 return new CharEnumerator (this);
2557 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2558 out bool left_align, out string format)
2560 int max = str.Length;
2562 // parses format specifier of form:
2563 // N,[\ +[-]M][:F]}
2565 // where:
2566 // N = argument number (non-negative integer)
2568 n = ParseDecimal (str, ref ptr);
2569 if (n < 0)
2570 throw new FormatException ("Input string was not in a correct format.");
2572 // M = width (non-negative integer)
2574 if (ptr < max && str[ptr] == ',') {
2575 // White space between ',' and number or sign.
2576 ++ptr;
2577 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2578 ++ptr;
2579 int start = ptr;
2581 format = str.Substring (start, ptr - start);
2583 left_align = (ptr < max && str [ptr] == '-');
2584 if (left_align)
2585 ++ ptr;
2587 width = ParseDecimal (str, ref ptr);
2588 if (width < 0)
2589 throw new FormatException ("Input string was not in a correct format.");
2591 else {
2592 width = 0;
2593 left_align = false;
2594 format = Empty;
2597 // F = argument format (string)
2599 if (ptr < max && str[ptr] == ':') {
2600 int start = ++ ptr;
2601 while (ptr < max) {
2602 if (str [ptr] == '}') {
2603 if (ptr + 1 < max && str [ptr + 1] == '}') {
2604 ++ptr;
2605 format += str.Substring (start, ptr - start);
2606 ++ptr;
2607 start = ptr;
2608 continue;
2611 break;
2614 ++ptr;
2617 format += str.Substring (start, ptr - start);
2619 else
2620 format = null;
2622 if ((ptr >= max) || str[ptr ++] != '}')
2623 throw new FormatException ("Input string was not in a correct format.");
2626 private static int ParseDecimal (string str, ref int ptr)
2628 int p = ptr;
2629 int n = 0;
2630 int max = str.Length;
2632 while (p < max) {
2633 char c = str[p];
2634 if (c < '0' || '9' < c)
2635 break;
2637 n = n * 10 + c - '0';
2638 ++ p;
2641 if (p == ptr || p == max)
2642 return -1;
2644 ptr = p;
2645 return n;
2648 internal unsafe void InternalSetChar (int idx, char val)
2650 if ((uint) idx >= (uint) Length)
2651 throw new ArgumentOutOfRangeException ("idx");
2653 fixed (char * pStr = &start_char)
2655 pStr [idx] = val;
2659 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2660 internal extern void InternalSetLength (int newLength);
2662 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2663 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2664 public unsafe override int GetHashCode ()
2666 fixed (char * c = this) {
2667 char * cc = c;
2668 char * end = cc + length - 1;
2669 int h = 0;
2670 for (;cc < end; cc += 2) {
2671 h = (h << 5) - h + *cc;
2672 h = (h << 5) - h + cc [1];
2674 ++end;
2675 if (cc < end)
2676 h = (h << 5) - h + *cc;
2677 return h;
2681 [ComVisible(false)]
2682 public static string Concat (IEnumerable<string> values)
2684 if (values == null)
2685 throw new ArgumentNullException ("values");
2687 var stringList = new List<string> ();
2688 int len = 0;
2689 foreach (var v in values){
2690 if (v == null)
2691 continue;
2692 len += v.Length;
2693 if (len < 0)
2694 throw new OutOfMemoryException ();
2695 stringList.Add (v);
2697 return ConcatInternal (stringList.ToArray (), len);
2700 [ComVisibleAttribute(false)]
2701 public static string Concat<T> (IEnumerable<T> values)
2703 if (values == null)
2704 throw new ArgumentNullException ("values");
2706 var stringList = new List<string> ();
2707 int len = 0;
2708 foreach (var v in values){
2709 string sr = v.ToString ();
2710 len += sr.Length;
2711 if (len < 0)
2712 throw new OutOfMemoryException ();
2713 stringList.Add (sr);
2715 return ConcatInternal (stringList.ToArray (), len);
2718 [ComVisibleAttribute(false)]
2719 public static string Join (string separator, IEnumerable<string> values)
2721 if (separator == null)
2722 return Concat (values);
2724 if (values == null)
2725 throw new ArgumentNullException ("values");
2727 var stringList = new List<string> (values);
2729 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2732 [ComVisibleAttribute(false)]
2733 public static string Join (string separator, params object [] values)
2735 if (separator == null)
2736 return Concat (values);
2738 if (values == null)
2739 throw new ArgumentNullException ("values");
2741 var strCopy = new string [values.Length];
2742 int i = 0;
2743 foreach (var v in values)
2744 strCopy [i++] = v.ToString ();
2746 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2749 [ComVisible (false)]
2750 public static string Join<T> (string separator, IEnumerable<T> values)
2752 if (separator == null)
2753 return Concat<T> (values);
2755 if (values == null)
2756 throw new ArgumentNullException ("values");
2758 var stringList = values as IList<T> ?? new List<T> (values);
2759 var strCopy = new string [stringList.Count];
2760 int i = 0;
2761 foreach (var v in stringList)
2762 strCopy [i++] = v.ToString ();
2764 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2767 public static bool IsNullOrWhiteSpace (string value)
2769 if ((value == null) || (value.Length == 0))
2770 return true;
2771 foreach (char c in value)
2772 if (!Char.IsWhiteSpace (c))
2773 return false;
2774 return true;
2777 internal unsafe int GetCaseInsensitiveHashCode ()
2779 fixed (char * c = this) {
2780 char * cc = c;
2781 char * end = cc + length - 1;
2782 int h = 0;
2783 for (;cc < end; cc += 2) {
2784 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2785 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2787 ++end;
2788 if (cc < end)
2789 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2790 return h;
2794 // Certain constructors are redirected to CreateString methods with
2795 // matching argument list. The this pointer should not be used.
2797 private unsafe String CreateString (sbyte* value)
2799 if (value == null)
2800 return Empty;
2802 byte* bytes = (byte*) value;
2803 int length = 0;
2805 try {
2806 while (bytes++ [0] != 0)
2807 length++;
2808 } catch (NullReferenceException) {
2809 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2812 return CreateString (value, 0, length, null);
2815 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2817 return CreateString (value, startIndex, length, null);
2820 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2822 if (length < 0)
2823 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2824 if (startIndex < 0)
2825 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2826 if (value + startIndex < value)
2827 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2829 if (enc == null) {
2830 if (value == null)
2831 throw new ArgumentNullException ("value");
2832 if (length == 0)
2833 return Empty;
2835 enc = Encoding.Default;
2838 byte [] bytes = new byte [length];
2840 if (length != 0)
2841 fixed (byte* bytePtr = bytes)
2842 try {
2843 if (value == null)
2844 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2845 memcpy (bytePtr, (byte*) (value + startIndex), length);
2846 } catch (NullReferenceException) {
2847 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2850 // GetString () is called even when length == 0
2851 return enc.GetString (bytes);
2854 unsafe string CreateString (char *value)
2856 if (value == null)
2857 return Empty;
2858 char *p = value;
2859 int i = 0;
2860 while (*p != 0) {
2861 ++i;
2862 ++p;
2864 string result = InternalAllocateStr (i);
2866 if (i != 0) {
2867 fixed (char *dest = result) {
2868 CharCopy (dest, value, i);
2871 return result;
2874 unsafe string CreateString (char *value, int startIndex, int length)
2876 if (length == 0)
2877 return Empty;
2878 if (value == null)
2879 throw new ArgumentNullException ("value");
2880 if (startIndex < 0)
2881 throw new ArgumentOutOfRangeException ("startIndex");
2882 if (length < 0)
2883 throw new ArgumentOutOfRangeException ("length");
2885 string result = InternalAllocateStr (length);
2887 fixed (char *dest = result) {
2888 CharCopy (dest, value + startIndex, length);
2890 return result;
2893 unsafe string CreateString (char [] val, int startIndex, int length)
2895 if (val == null)
2896 throw new ArgumentNullException ("value");
2897 if (startIndex < 0)
2898 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2899 if (length < 0)
2900 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2901 if (startIndex > val.Length - length)
2902 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2903 if (length == 0)
2904 return Empty;
2906 string result = InternalAllocateStr (length);
2908 fixed (char *dest = result, src = val) {
2909 CharCopy (dest, src + startIndex, length);
2911 return result;
2914 unsafe string CreateString (char [] val)
2916 if (val == null || val.Length == 0)
2917 return Empty;
2918 string result = InternalAllocateStr (val.Length);
2920 fixed (char *dest = result, src = val) {
2921 CharCopy (dest, src, val.Length);
2923 return result;
2926 unsafe string CreateString (char c, int count)
2928 if (count < 0)
2929 throw new ArgumentOutOfRangeException ("count");
2930 if (count == 0)
2931 return Empty;
2932 string result = InternalAllocateStr (count);
2933 fixed (char *dest = result) {
2934 char *p = dest;
2935 char *end = p + count;
2936 while (p < end) {
2937 *p = c;
2938 p++;
2941 return result;
2944 /* helpers used by the runtime as well as above or eslewhere in corlib */
2945 internal static unsafe void memset (byte *dest, int val, int len)
2947 if (len < 8) {
2948 while (len != 0) {
2949 *dest = (byte)val;
2950 ++dest;
2951 --len;
2953 return;
2955 if (val != 0) {
2956 val = val | (val << 8);
2957 val = val | (val << 16);
2959 // align to 4
2960 int rest = (int)dest & 3;
2961 if (rest != 0) {
2962 rest = 4 - rest;
2963 len -= rest;
2964 do {
2965 *dest = (byte)val;
2966 ++dest;
2967 --rest;
2968 } while (rest != 0);
2970 while (len >= 16) {
2971 ((int*)dest) [0] = val;
2972 ((int*)dest) [1] = val;
2973 ((int*)dest) [2] = val;
2974 ((int*)dest) [3] = val;
2975 dest += 16;
2976 len -= 16;
2978 while (len >= 4) {
2979 ((int*)dest) [0] = val;
2980 dest += 4;
2981 len -= 4;
2983 // tail bytes
2984 while (len > 0) {
2985 *dest = (byte)val;
2986 dest++;
2987 len--;
2991 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2992 /*while (size >= 32) {
2993 // using long is better than int and slower than double
2994 // FIXME: enable this only on correct alignment or on platforms
2995 // that can tolerate unaligned reads/writes of doubles
2996 ((double*)dest) [0] = ((double*)src) [0];
2997 ((double*)dest) [1] = ((double*)src) [1];
2998 ((double*)dest) [2] = ((double*)src) [2];
2999 ((double*)dest) [3] = ((double*)src) [3];
3000 dest += 32;
3001 src += 32;
3002 size -= 32;
3004 while (size >= 16) {
3005 ((int*)dest) [0] = ((int*)src) [0];
3006 ((int*)dest) [1] = ((int*)src) [1];
3007 ((int*)dest) [2] = ((int*)src) [2];
3008 ((int*)dest) [3] = ((int*)src) [3];
3009 dest += 16;
3010 src += 16;
3011 size -= 16;
3013 while (size >= 4) {
3014 ((int*)dest) [0] = ((int*)src) [0];
3015 dest += 4;
3016 src += 4;
3017 size -= 4;
3019 while (size > 0) {
3020 ((byte*)dest) [0] = ((byte*)src) [0];
3021 dest += 1;
3022 src += 1;
3023 --size;
3026 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3027 while (size >= 8) {
3028 ((short*)dest) [0] = ((short*)src) [0];
3029 ((short*)dest) [1] = ((short*)src) [1];
3030 ((short*)dest) [2] = ((short*)src) [2];
3031 ((short*)dest) [3] = ((short*)src) [3];
3032 dest += 8;
3033 src += 8;
3034 size -= 8;
3036 while (size >= 2) {
3037 ((short*)dest) [0] = ((short*)src) [0];
3038 dest += 2;
3039 src += 2;
3040 size -= 2;
3042 if (size > 0)
3043 ((byte*)dest) [0] = ((byte*)src) [0];
3045 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3046 while (size >= 8) {
3047 ((byte*)dest) [0] = ((byte*)src) [0];
3048 ((byte*)dest) [1] = ((byte*)src) [1];
3049 ((byte*)dest) [2] = ((byte*)src) [2];
3050 ((byte*)dest) [3] = ((byte*)src) [3];
3051 ((byte*)dest) [4] = ((byte*)src) [4];
3052 ((byte*)dest) [5] = ((byte*)src) [5];
3053 ((byte*)dest) [6] = ((byte*)src) [6];
3054 ((byte*)dest) [7] = ((byte*)src) [7];
3055 dest += 8;
3056 src += 8;
3057 size -= 8;
3059 while (size >= 2) {
3060 ((byte*)dest) [0] = ((byte*)src) [0];
3061 ((byte*)dest) [1] = ((byte*)src) [1];
3062 dest += 2;
3063 src += 2;
3064 size -= 2;
3066 if (size > 0)
3067 ((byte*)dest) [0] = ((byte*)src) [0];
3070 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3071 // FIXME: if pointers are not aligned, try to align them
3072 // so a faster routine can be used. Handle the case where
3073 // the pointers can't be reduced to have the same alignment
3074 // (just ignore the issue on x86?)
3075 if ((((int)dest | (int)src) & 3) != 0) {
3076 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3077 dest [0] = src [0];
3078 ++dest;
3079 ++src;
3080 --size;
3082 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3083 ((short*)dest) [0] = ((short*)src) [0];
3084 dest += 2;
3085 src += 2;
3086 size -= 2;
3088 if ((((int)dest | (int)src) & 1) != 0) {
3089 memcpy1 (dest, src, size);
3090 return;
3092 if ((((int)dest | (int)src) & 2) != 0) {
3093 memcpy2 (dest, src, size);
3094 return;
3097 memcpy4 (dest, src, size);
3100 /* Used by the runtime */
3101 internal static unsafe void bzero (byte *dest, int len) {
3102 memset (dest, 0, len);
3105 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3106 ((byte*)dest) [0] = 0;
3109 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3110 ((short*)dest) [0] = 0;
3113 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3114 ((int*)dest) [0] = 0;
3117 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3118 ((long*)dest) [0] = 0;
3121 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3122 ((byte*)dest) [0] = ((byte*)src) [0];
3125 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3126 ((short*)dest) [0] = ((short*)src) [0];
3129 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3130 ((int*)dest) [0] = ((int*)src) [0];
3133 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3134 ((long*)dest) [0] = ((long*)src) [0];
3137 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3138 // Same rules as for memcpy, but with the premise that
3139 // chars can only be aligned to even addresses if their
3140 // enclosing types are correctly aligned
3141 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3142 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3143 ((short*)dest) [0] = ((short*)src) [0];
3144 dest++;
3145 src++;
3146 count--;
3148 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3149 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3150 return;
3153 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3156 internal static unsafe void wstrcpy (char *dmem, char *smem, int charCount)
3158 CharCopy (dmem, smem, charCount);
3161 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3163 dest += count;
3164 src += count;
3165 for (int i = count; i > 0; i--) {
3166 dest--;
3167 src--;
3168 *dest = *src;
3172 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3174 fixed (char* dest = target, src = source)
3175 CharCopy (dest + targetIndex, src + sourceIndex, count);
3178 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3180 fixed (char* dest = target, src = source)
3181 CharCopy (dest + targetIndex, src + sourceIndex, count);
3184 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3185 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3187 fixed (char* dest = target, src = source)
3188 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3191 internal static String FastAllocateString (int length)
3193 return InternalAllocateStr (length);
3196 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3197 unsafe public extern String (char *value);
3199 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3200 unsafe public extern String (char *value, int startIndex, int length);
3202 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3203 unsafe public extern String (sbyte *value);
3205 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3206 unsafe public extern String (sbyte *value, int startIndex, int length);
3208 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3209 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3211 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3212 public extern String (char [] value, int startIndex, int length);
3214 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3215 public extern String (char [] value);
3217 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3218 public extern String (char c, int count);
3220 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3221 internal extern static String InternalAllocateStr (int length);
3223 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3224 private extern static string InternalIntern (string str);
3226 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3227 private extern static string InternalIsInterned (string str);
3229 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3230 private extern static int GetLOSLimit ();
3232 #region "from referencesource" // and actually we replaced some parts.
3234 // Helper for encodings so they can talk to our buffer directly
3235 // stringLength must be the exact size we'll expect
3236 [System.Security.SecurityCritical] // auto-generated
3237 unsafe static internal String CreateStringFromEncoding(
3238 byte* bytes, int byteLength, Encoding encoding)
3240 Contract.Requires(bytes != null);
3241 Contract.Requires(byteLength >= 0);
3243 // Get our string length
3244 int stringLength = encoding.GetCharCount(bytes, byteLength, null);
3245 Contract.Assert(stringLength >= 0, "stringLength >= 0");
3247 // They gave us an empty string if they needed one
3248 // 0 bytelength might be possible if there's something in an encoder
3249 if (stringLength == 0)
3250 return String.Empty;
3252 String s = FastAllocateString(stringLength);
3253 fixed(char* pTempChars = &s.start_char)
3255 int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
3256 Contract.Assert(stringLength == doubleCheck,
3257 "Expected encoding.GetChars to return same length as encoding.GetCharCount");
3260 return s;
3263 // our own implementation for CLR icall.
3264 unsafe internal static int nativeCompareOrdinalIgnoreCaseWC (string name, sbyte *strBBytes)
3266 for (int i = 0; i < name.Length; i++) {
3267 sbyte b = *(strBBytes + i);
3268 if (b < 0)
3269 throw new ArgumentException ();
3270 int ret = char.ToUpper ((char) b) - char.ToUpper (name [i]);
3271 if (ret != 0)
3272 return ret;
3274 return 0;
3276 #endregion