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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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
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
;
58 public sealed class String
: IConvertible
, ICloneable
, IEnumerable
, IComparable
, IComparable
<String
>, IEquatable
<String
>, IEnumerable
<char>
60 [NonSerialized
] private int length
;
61 [NonSerialized
] private char start_char
;
63 public static readonly String Empty
= "";
65 internal static readonly int LOS_limit
= GetLOSLimit ();
67 public static unsafe bool Equals (string a
, string b
)
69 if ((a
as object) == (b
as object))
72 if (a
== null || b
== null)
80 fixed (char* s1
= &a
.start_char
, s2
= &b
.start_char
) {
85 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0] ||
86 ((int*)s1_ptr
)[1] != ((int*)s2_ptr
)[1] ||
87 ((int*)s1_ptr
)[2] != ((int*)s2_ptr
)[2] ||
88 ((int*)s1_ptr
)[3] != ((int*)s2_ptr
)[3])
97 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0] ||
98 ((int*)s1_ptr
)[1] != ((int*)s2_ptr
)[1])
107 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0])
115 return len
== 0 || *s1_ptr
== *s2_ptr
;
119 public static bool operator == (String a
, String b
)
121 return Equals (a
, b
);
124 public static bool operator != (String a
, String b
)
126 return !Equals (a
, b
);
129 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
130 public override bool Equals (Object obj
)
132 return Equals (this, obj
as String
);
135 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
136 public bool Equals (String
value)
138 return Equals (this, value);
141 [IndexerName ("Chars")]
142 public unsafe char this [int index
] {
144 if (index
< 0 || index
>= length
)
145 throw new IndexOutOfRangeException ();
146 fixed (char* c
= &start_char
)
151 public Object
Clone ()
156 public TypeCode
GetTypeCode ()
158 return TypeCode
.String
;
161 public unsafe void CopyTo (int sourceIndex
, char[] destination
, int destinationIndex
, int count
)
163 if (destination
== null)
164 throw new ArgumentNullException ("destination");
166 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
167 if (destinationIndex
< 0)
168 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
170 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
171 if (sourceIndex
> Length
- count
)
172 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
173 if (destinationIndex
> destination
.Length
- count
)
174 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
176 fixed (char* dest
= destination
, src
= this)
177 CharCopy (dest
+ destinationIndex
, src
+ sourceIndex
, count
);
180 public char[] ToCharArray ()
182 return ToCharArray (0, length
);
185 public unsafe char[] ToCharArray (int startIndex
, int length
)
188 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
190 throw new ArgumentOutOfRangeException ("length", "< 0");
191 if (startIndex
> this.length
- length
)
192 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
194 char[] tmp
= new char [length
];
195 fixed (char* dest
= tmp
, src
= this)
196 CharCopy (dest
, src
+ startIndex
, length
);
200 public String
[] Split (params char [] separator
)
202 return Split (separator
, Int32
.MaxValue
);
205 public String
[] Split (char[] separator
, int count
)
207 if (separator
== null || separator
.Length
== 0)
208 separator
= WhiteChars
;
211 throw new ArgumentOutOfRangeException ("count");
214 return new String
[0];
217 return new String
[1] { this }
;
219 return InternalSplit (separator
, count
, 0);
223 [MonoDocumentationNote ("code should be moved to managed")]
224 public String
[] Split (char[] separator
, int count
, StringSplitOptions options
)
226 if (separator
== null || separator
.Length
== 0)
227 return Split (WhiteChars
, count
, options
);
230 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
231 if ((options
!= StringSplitOptions
.None
) && (options
!= StringSplitOptions
.RemoveEmptyEntries
))
232 throw new ArgumentException ("Illegal enum value: " + options
+ ".");
235 return new string [0];
237 return InternalSplit (separator
, count
, (int)options
);
241 public String
[] Split (string[] separator
, int count
, StringSplitOptions options
)
243 if (separator
== null || separator
.Length
== 0)
244 return Split (WhiteChars
, count
, options
);
247 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
248 if ((options
!= StringSplitOptions
.None
) && (options
!= StringSplitOptions
.RemoveEmptyEntries
))
249 throw new ArgumentException ("Illegal enum value: " + options
+ ".");
251 bool removeEmpty
= (options
& StringSplitOptions
.RemoveEmptyEntries
) == StringSplitOptions
.RemoveEmptyEntries
;
253 if (count
== 0 || (this == String
.Empty
&& removeEmpty
))
254 return new String
[0];
256 ArrayList arr
= new ArrayList ();
260 while (pos
< this.Length
) {
262 int matchPos
= Int32
.MaxValue
;
264 // Find the first position where any of the separators matches
265 for (int i
= 0; i
< separator
.Length
; ++i
) {
266 string sep
= separator
[i
];
267 if (sep
== null || sep
== String
.Empty
)
270 int match
= IndexOf (sep
, pos
);
271 if (match
> -1 && match
< matchPos
) {
277 if (matchIndex
== -1)
280 if (!(matchPos
== pos
&& removeEmpty
))
281 arr
.Add (this.Substring (pos
, matchPos
- pos
));
283 pos
= matchPos
+ separator
[matchIndex
].Length
;
287 if (matchCount
== count
- 1)
292 return new String
[] { this }
;
294 if (removeEmpty
&& pos
== this.Length
) {
295 String
[] res
= new String
[arr
.Count
];
296 arr
.CopyTo (0, res
, 0, arr
.Count
);
301 String
[] res
= new String
[arr
.Count
+ 1];
302 arr
.CopyTo (0, res
, 0, arr
.Count
);
303 res
[arr
.Count
] = this.Substring (pos
);
311 public String
[] Split (char[] separator
, StringSplitOptions options
)
313 return Split (separator
, Int32
.MaxValue
, options
);
317 public String
[] Split (String
[] separator
, StringSplitOptions options
)
319 return Split (separator
, Int32
.MaxValue
, options
);
322 public String
Substring (int startIndex
)
326 if (startIndex
< 0 || startIndex
> this.length
)
327 throw new ArgumentOutOfRangeException ("startIndex");
329 return SubstringUnchecked (startIndex
, this.length
- startIndex
);
332 public String
Substring (int startIndex
, int length
)
335 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
337 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
338 if (startIndex
> this.length
)
339 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
340 if (startIndex
> this.length
- length
)
341 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
342 if (startIndex
== 0 && length
== this.length
)
345 return SubstringUnchecked (startIndex
, length
);
348 // This method is used by StringBuilder.ToString() and is expected to
349 // always create a new string object (or return String.Empty).
350 internal unsafe String
SubstringUnchecked (int startIndex
, int length
)
355 string tmp
= InternalAllocateStr (length
);
356 fixed (char* dest
= tmp
, src
= this) {
357 CharCopy (dest
, src
+ startIndex
, length
);
362 private static readonly char[] WhiteChars
= {
363 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
364 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
365 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
366 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
367 (char) 0x3000, (char) 0xFEFF,
370 (char) 0x202f, (char) 0x205f,
374 public String
Trim ()
378 int start
= FindNotWhiteSpace (0, length
, 1);
383 int end
= FindNotWhiteSpace (length
- 1, start
, -1);
385 int newLength
= end
- start
+ 1;
386 if (newLength
== length
)
389 return SubstringUnchecked (start
, newLength
);
392 public String
Trim (params char[] trimChars
)
394 if (trimChars
== null || trimChars
.Length
== 0)
399 int start
= FindNotInTable (0, length
, 1, trimChars
);
404 int end
= FindNotInTable (length
- 1, start
, -1, trimChars
);
406 int newLength
= end
- start
+ 1;
407 if (newLength
== length
)
410 return SubstringUnchecked (start
, newLength
);
413 public String
TrimStart (params char[] trimChars
)
418 if (trimChars
== null || trimChars
.Length
== 0)
419 start
= FindNotWhiteSpace (0, length
, 1);
421 start
= FindNotInTable (0, length
, 1, trimChars
);
426 return SubstringUnchecked (start
, length
- start
);
429 public String
TrimEnd (params char[] trimChars
)
434 if (trimChars
== null || trimChars
.Length
== 0)
435 end
= FindNotWhiteSpace (length
- 1, -1, -1);
437 end
= FindNotInTable (length
- 1, -1, -1, trimChars
);
443 return SubstringUnchecked (0, end
);
446 private int FindNotWhiteSpace (int pos
, int target
, int change
)
448 while (pos
!= target
) {
452 if (c
< 0x9 || c
> 0xD)
457 if (c
!= 0xA0 && c
!= 0xFEFF && c
!= 0x3000) {
458 if (c
!= 0x85 && c
!= 0x1680 && c
!= 0x2028 && c
!= 0x2029
460 // On Silverlight this whitespace participates in Trim
461 && c
!= 0x202f && c
!= 0x205f
464 if (c
< 0x2000 || c
> 0x200B)
473 private unsafe int FindNotInTable (int pos
, int target
, int change
, char[] table
)
475 fixed (char* tablePtr
= table
, thisPtr
= this) {
476 while (pos
!= target
) {
477 char c
= thisPtr
[pos
];
479 while (x
< table
.Length
) {
480 if (c
== tablePtr
[x
])
484 if (x
== table
.Length
)
492 public static int Compare (String strA
, String strB
)
494 return CultureInfo
.CurrentCulture
.CompareInfo
.Compare (strA
, strB
, CompareOptions
.None
);
497 public static int Compare (String strA
, String strB
, bool ignoreCase
)
499 return CultureInfo
.CurrentCulture
.CompareInfo
.Compare (strA
, strB
, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
502 public static int Compare (String strA
, String strB
, bool ignoreCase
, CultureInfo culture
)
505 throw new ArgumentNullException ("culture");
507 return culture
.CompareInfo
.Compare (strA
, strB
, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
510 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
)
512 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.CurrentCulture
);
515 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
, bool ignoreCase
)
517 return Compare (strA
, indexA
, strB
, indexB
, length
, ignoreCase
, CultureInfo
.CurrentCulture
);
520 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
, bool ignoreCase
, CultureInfo culture
)
523 throw new ArgumentNullException ("culture");
525 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
526 throw new ArgumentOutOfRangeException ();
538 else if (strB
== null) {
542 CompareOptions compopts
;
545 compopts
= CompareOptions
.IgnoreCase
;
547 compopts
= CompareOptions
.None
;
549 // Need to cap the requested length to the
550 // length of the string, because
551 // CompareInfo.Compare will insist that length
552 // <= (string.Length - offset)
557 if (length
> (strA
.Length
- indexA
)) {
558 len1
= strA
.Length
- indexA
;
561 if (length
> (strB
.Length
- indexB
)) {
562 len2
= strB
.Length
- indexB
;
565 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
566 return culture
.CompareInfo
.Compare (strA
, indexA
, len1
, strB
, indexB
, len2
, compopts
);
569 public static int Compare (string strA
, string strB
, StringComparison comparisonType
)
571 switch (comparisonType
) {
572 case StringComparison
.CurrentCulture
:
573 return Compare (strA
, strB
, false, CultureInfo
.CurrentCulture
);
574 case StringComparison
.CurrentCultureIgnoreCase
:
575 return Compare (strA
, strB
, true, CultureInfo
.CurrentCulture
);
576 case StringComparison
.InvariantCulture
:
577 return Compare (strA
, strB
, false, CultureInfo
.InvariantCulture
);
578 case StringComparison
.InvariantCultureIgnoreCase
:
579 return Compare (strA
, strB
, true, CultureInfo
.InvariantCulture
);
580 case StringComparison
.Ordinal
:
581 return CompareOrdinalUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
582 case StringComparison
.OrdinalIgnoreCase
:
583 return CompareOrdinalCaseInsensitiveUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
585 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
586 throw new ArgumentException (msg
, "comparisonType");
590 public static int Compare (string strA
, int indexA
, string strB
, int indexB
, int length
, StringComparison comparisonType
)
592 switch (comparisonType
) {
593 case StringComparison
.CurrentCulture
:
594 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.CurrentCulture
);
595 case StringComparison
.CurrentCultureIgnoreCase
:
596 return Compare (strA
, indexA
, strB
, indexB
, length
, true, CultureInfo
.CurrentCulture
);
597 case StringComparison
.InvariantCulture
:
598 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.InvariantCulture
);
599 case StringComparison
.InvariantCultureIgnoreCase
:
600 return Compare (strA
, indexA
, strB
, indexB
, length
, true, CultureInfo
.InvariantCulture
);
601 case StringComparison
.Ordinal
:
602 return CompareOrdinal (strA
, indexA
, strB
, indexB
, length
);
603 case StringComparison
.OrdinalIgnoreCase
:
604 return CompareOrdinalCaseInsensitive (strA
, indexA
, strB
, indexB
, length
);
606 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
607 throw new ArgumentException (msg
, "comparisonType");
611 public static bool Equals (string a
, string b
, StringComparison comparisonType
)
613 return String
.Compare (a
, b
, comparisonType
) == 0;
616 public bool Equals (string value, StringComparison comparisonType
)
618 return String
.Compare (value, this, comparisonType
) == 0;
621 public static int Compare (string strA
, string strB
, CultureInfo culture
, CompareOptions options
)
624 throw new ArgumentNullException ("culture");
626 return culture
.CompareInfo
.Compare (strA
, strB
, options
);
629 public static int Compare (string strA
, int indexA
, string strB
, int indexB
, int length
, CultureInfo culture
, CompareOptions options
)
632 throw new ArgumentNullException ("culture");
637 if (length
> (strA
.Length
- indexA
))
638 len1
= strA
.Length
- indexA
;
640 if (length
> (strB
.Length
- indexB
))
641 len2
= strB
.Length
- indexB
;
643 return culture
.CompareInfo
.Compare (strA
, indexA
, len1
, strB
, indexB
, len2
, options
);
646 public int CompareTo (Object
value)
651 if (!(value is String
))
652 throw new ArgumentException ();
654 return String
.Compare (this, (String
) value);
657 public int CompareTo (String strB
)
662 return Compare (this, strB
);
665 public static int CompareOrdinal (String strA
, String strB
)
667 return CompareOrdinalUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
670 public static int CompareOrdinal (String strA
, int indexA
, String strB
, int indexB
, int length
)
672 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
673 throw new ArgumentOutOfRangeException ();
675 return CompareOrdinalUnchecked (strA
, indexA
, length
, strB
, indexB
, length
);
678 internal static int CompareOrdinalCaseInsensitive (String strA
, int indexA
, String strB
, int indexB
, int length
)
680 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
681 throw new ArgumentOutOfRangeException ();
683 return CompareOrdinalCaseInsensitiveUnchecked (strA
, indexA
, length
, strB
, indexB
, length
);
686 internal static unsafe int CompareOrdinalUnchecked (String strA
, int indexA
, int lenA
, String strB
, int indexB
, int lenB
)
693 } else if (strB
== null) {
696 int lengthA
= Math
.Min (lenA
, strA
.Length
- indexA
);
697 int lengthB
= Math
.Min (lenB
, strB
.Length
- indexB
);
699 if (lengthA
== lengthB
&& Object
.ReferenceEquals (strA
, strB
))
702 fixed (char* aptr
= strA
, bptr
= strB
) {
703 char* ap
= aptr
+ indexA
;
704 char* end
= ap
+ Math
.Min (lengthA
, lengthB
);
705 char* bp
= bptr
+ indexB
;
712 return lengthA
- lengthB
;
716 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA
, int indexA
, int lenA
, String strB
, int indexB
, int lenB
)
718 // Same as above, but checks versus uppercase characters
724 } else if (strB
== null) {
727 int lengthA
= Math
.Min (lenA
, strA
.Length
- indexA
);
728 int lengthB
= Math
.Min (lenB
, strB
.Length
- indexB
);
730 if (lengthA
== lengthB
&& Object
.ReferenceEquals (strA
, strB
))
733 fixed (char* aptr
= strA
, bptr
= strB
) {
734 char* ap
= aptr
+ indexA
;
735 char* end
= ap
+ Math
.Min (lengthA
, lengthB
);
736 char* bp
= bptr
+ indexB
;
739 char c1
= Char
.ToUpperInvariant (*ap
);
740 char c2
= Char
.ToUpperInvariant (*bp
);
747 return lengthA
- lengthB
;
751 public bool EndsWith (String
value)
754 throw new ArgumentNullException ("value");
756 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
759 public bool EndsWith (String
value, bool ignoreCase
, CultureInfo culture
)
762 throw new ArgumentNullException ("value");
764 culture
= CultureInfo
.CurrentCulture
;
766 return culture
.CompareInfo
.IsSuffix (this, value,
767 ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
770 // Following methods are culture-insensitive
771 public int IndexOfAny (char [] anyOf
)
774 throw new ArgumentNullException ();
775 if (this.length
== 0)
778 return IndexOfAnyUnchecked (anyOf
, 0, this.length
);
781 public int IndexOfAny (char [] anyOf
, int startIndex
)
784 throw new ArgumentNullException ();
785 if (startIndex
< 0 || startIndex
> this.length
)
786 throw new ArgumentOutOfRangeException ();
788 return IndexOfAnyUnchecked (anyOf
, startIndex
, this.length
- startIndex
);
791 public int IndexOfAny (char [] anyOf
, int startIndex
, int count
)
794 throw new ArgumentNullException ();
795 if (startIndex
< 0 || startIndex
> this.length
)
796 throw new ArgumentOutOfRangeException ();
797 if (count
< 0 || startIndex
> this.length
- count
)
798 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
800 return IndexOfAnyUnchecked (anyOf
, startIndex
, count
);
803 private unsafe int IndexOfAnyUnchecked (char[] anyOf
, int startIndex
, int count
)
805 if (anyOf
.Length
== 0)
808 if (anyOf
.Length
== 1)
809 return IndexOfUnchecked (anyOf
[0], startIndex
, count
);
811 fixed (char* any
= anyOf
) {
815 char* end_any_ptr
= any
+ anyOf
.Length
;
817 while (++any_ptr
!= end_any_ptr
) {
818 if (*any_ptr
> highest
) {
823 if (*any_ptr
< lowest
)
827 fixed (char* start
= &start_char
) {
828 char* ptr
= start
+ startIndex
;
829 char* end_ptr
= ptr
+ count
;
831 while (ptr
!= end_ptr
) {
832 if (*ptr
> highest
|| *ptr
< lowest
) {
838 return (int)(ptr
- start
);
841 while (++any_ptr
!= end_any_ptr
) {
842 if (*ptr
== *any_ptr
)
843 return (int)(ptr
- start
);
854 public int IndexOf (string value, StringComparison comparisonType
)
856 return IndexOf (value, 0, this.Length
, comparisonType
);
859 public int IndexOf (string value, int startIndex
, StringComparison comparisonType
)
861 return IndexOf (value, startIndex
, this.Length
- startIndex
, comparisonType
);
864 public int IndexOf (string value, int startIndex
, int count
, StringComparison comparisonType
)
866 switch (comparisonType
) {
867 case StringComparison
.CurrentCulture
:
868 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
869 case StringComparison
.CurrentCultureIgnoreCase
:
870 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
871 case StringComparison
.InvariantCulture
:
872 return CultureInfo
.InvariantCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
873 case StringComparison
.InvariantCultureIgnoreCase
:
874 return CultureInfo
.InvariantCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
875 case StringComparison
.Ordinal
:
876 return IndexOfOrdinal (value, startIndex
, count
, CompareOptions
.Ordinal
);
877 case StringComparison
.OrdinalIgnoreCase
:
878 return IndexOfOrdinal (value, startIndex
, count
, CompareOptions
.OrdinalIgnoreCase
);
880 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
881 throw new ArgumentException (msg
, "comparisonType");
885 internal int IndexOfOrdinal (string value, int startIndex
, int count
, CompareOptions options
)
888 throw new ArgumentNullException ("value");
890 throw new ArgumentOutOfRangeException ("startIndex");
891 if (count
< 0 || (this.length
- startIndex
) < count
)
892 throw new ArgumentOutOfRangeException ("count");
894 if (options
== CompareOptions
.Ordinal
)
895 return IndexOfOrdinalUnchecked (value, startIndex
, count
);
896 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex
, count
);
899 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex
, int count
)
901 int valueLen
= value.Length
;
902 if (count
< valueLen
)
907 return IndexOfUnchecked (value[0], startIndex
, count
);
911 fixed (char* thisptr
= this, valueptr
= value) {
912 char* ap
= thisptr
+ startIndex
;
913 char* thisEnd
= ap
+ count
- valueLen
+ 1;
914 while (ap
!= thisEnd
) {
915 if (*ap
== *valueptr
) {
916 for (int i
= 1; i
< valueLen
; i
++) {
917 if (ap
[i
] != valueptr
[i
])
920 return (int)(ap
- thisptr
);
929 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex
, int count
)
931 int valueLen
= value.Length
;
932 if (count
< valueLen
)
938 fixed (char* thisptr
= this, valueptr
= value) {
939 char* ap
= thisptr
+ startIndex
;
940 char* thisEnd
= ap
+ count
- valueLen
+ 1;
941 while (ap
!= thisEnd
) {
942 for (int i
= 0; i
< valueLen
; i
++) {
943 if (Char
.ToUpperInvariant (ap
[i
]) != Char
.ToUpperInvariant (valueptr
[i
]))
946 return (int)(ap
- thisptr
);
954 public int LastIndexOf (string value, StringComparison comparisonType
)
956 if (this.Length
== 0)
957 return value == String
.Empty
? 0 : -1;
959 return LastIndexOf (value, this.Length
- 1, this.Length
, comparisonType
);
962 public int LastIndexOf (string value, int startIndex
, StringComparison comparisonType
)
964 return LastIndexOf (value, startIndex
, startIndex
+ 1, comparisonType
);
967 public int LastIndexOf (string value, int startIndex
, int count
, StringComparison comparisonType
)
969 switch (comparisonType
) {
970 case StringComparison
.CurrentCulture
:
971 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
972 case StringComparison
.CurrentCultureIgnoreCase
:
973 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
974 case StringComparison
.InvariantCulture
:
975 return CultureInfo
.InvariantCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
976 case StringComparison
.InvariantCultureIgnoreCase
:
977 return CultureInfo
.InvariantCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
978 case StringComparison
.Ordinal
:
979 return LastIndexOfOrdinal (value, startIndex
, count
, CompareOptions
.Ordinal
);
980 case StringComparison
.OrdinalIgnoreCase
:
981 return LastIndexOfOrdinal (value, startIndex
, count
, CompareOptions
.OrdinalIgnoreCase
);
983 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
984 throw new ArgumentException (msg
, "comparisonType");
988 internal int LastIndexOfOrdinal (string value, int startIndex
, int count
, CompareOptions options
)
991 throw new ArgumentNullException ("value");
992 if (startIndex
< 0 || startIndex
> length
)
993 throw new ArgumentOutOfRangeException ("startIndex");
994 if (count
< 0 || (startIndex
< count
- 1))
995 throw new ArgumentOutOfRangeException ("count");
997 if (options
== CompareOptions
.Ordinal
)
998 return LastIndexOfOrdinalUnchecked (value, startIndex
, count
);
999 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex
, count
);
1002 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex
, int count
)
1004 int valueLen
= value.Length
;
1005 if (count
< valueLen
)
1008 if (valueLen
<= 1) {
1010 return LastIndexOfUnchecked (value[0], startIndex
, count
);
1014 fixed (char* thisptr
= this, valueptr
= value) {
1015 char* ap
= thisptr
+ startIndex
- valueLen
+ 1;
1016 char* thisEnd
= ap
- count
+ valueLen
- 1;
1017 while (ap
!= thisEnd
) {
1018 if (*ap
== *valueptr
) {
1019 for (int i
= 1; i
< valueLen
; i
++) {
1020 if (ap
[i
] != valueptr
[i
])
1023 return (int)(ap
- thisptr
);
1032 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex
, int count
)
1034 int valueLen
= value.Length
;
1035 if (count
< valueLen
)
1041 fixed (char* thisptr
= this, valueptr
= value) {
1042 char* ap
= thisptr
+ startIndex
- valueLen
+ 1;
1043 char* thisEnd
= ap
- count
+ valueLen
- 1;
1044 while (ap
!= thisEnd
) {
1045 for (int i
= 0; i
< valueLen
; i
++) {
1046 if (Char
.ToUpperInvariant (ap
[i
]) != Char
.ToUpperInvariant (valueptr
[i
]))
1049 return (int)(ap
- thisptr
);
1057 // Following methods are culture-insensitive
1058 public int IndexOf (char value)
1060 if (this.length
== 0)
1063 return IndexOfUnchecked (value, 0, this.length
);
1066 public int IndexOf (char value, int startIndex
)
1069 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1070 if (startIndex
> this.length
)
1071 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1073 if ((startIndex
== 0 && this.length
== 0) || (startIndex
== this.length
))
1076 return IndexOfUnchecked (value, startIndex
, this.length
- startIndex
);
1079 public int IndexOf (char value, int startIndex
, int count
)
1081 if (startIndex
< 0 || startIndex
> this.length
)
1082 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1084 throw new ArgumentOutOfRangeException ("count", "< 0");
1085 if (startIndex
> this.length
- count
)
1086 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1088 if ((startIndex
== 0 && this.length
== 0) || (startIndex
== this.length
) || (count
== 0))
1091 return IndexOfUnchecked (value, startIndex
, count
);
1094 internal unsafe int IndexOfUnchecked (char value, int startIndex
, int count
)
1096 // It helps JIT compiler to optimize comparison
1097 int value_32
= (int)value;
1099 fixed (char* start
= &start_char
) {
1100 char* ptr
= start
+ startIndex
;
1101 char* end_ptr
= ptr
+ (count
>> 3 << 3);
1103 while (ptr
!= end_ptr
) {
1104 if (*ptr
== value_32
)
1105 return (int)(ptr
- start
);
1106 if (ptr
[1] == value_32
)
1107 return (int)(ptr
- start
+ 1);
1108 if (ptr
[2] == value_32
)
1109 return (int)(ptr
- start
+ 2);
1110 if (ptr
[3] == value_32
)
1111 return (int)(ptr
- start
+ 3);
1112 if (ptr
[4] == value_32
)
1113 return (int)(ptr
- start
+ 4);
1114 if (ptr
[5] == value_32
)
1115 return (int)(ptr
- start
+ 5);
1116 if (ptr
[6] == value_32
)
1117 return (int)(ptr
- start
+ 6);
1118 if (ptr
[7] == value_32
)
1119 return (int)(ptr
- start
+ 7);
1124 end_ptr
+= count
& 0x07;
1125 while (ptr
!= end_ptr
) {
1126 if (*ptr
== value_32
)
1127 return (int)(ptr
- start
);
1135 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex
, int count
)
1139 int end
= startIndex
+ count
;
1140 char c
= Char
.ToUpperInvariant (value);
1141 fixed (char* s
= &start_char
) {
1142 for (int i
= startIndex
; i
< end
; i
++)
1143 if (Char
.ToUpperInvariant (s
[i
]) == c
)
1149 // Following methods are culture-sensitive
1150 public int IndexOf (String
value)
1153 throw new ArgumentNullException ("value");
1154 if (value.length
== 0)
1156 if (this.length
== 0)
1158 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, 0, length
, CompareOptions
.Ordinal
);
1161 public int IndexOf (String
value, int startIndex
)
1163 return IndexOf (value, startIndex
, this.length
- startIndex
);
1166 public int IndexOf (String
value, int startIndex
, int count
)
1169 throw new ArgumentNullException ("value");
1170 if (startIndex
< 0 || startIndex
> this.length
)
1171 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1172 if (count
< 0 || startIndex
> this.length
- count
)
1173 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1175 if (value.length
== 0)
1178 if (startIndex
== 0 && this.length
== 0)
1184 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
);
1187 // Following methods are culture-insensitive
1188 public int LastIndexOfAny (char [] anyOf
)
1191 throw new ArgumentNullException ();
1193 return LastIndexOfAnyUnchecked (anyOf
, this.length
- 1, this.length
);
1196 public int LastIndexOfAny (char [] anyOf
, int startIndex
)
1199 throw new ArgumentNullException ();
1201 if (startIndex
< 0 || startIndex
>= this.length
)
1202 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1204 if (this.length
== 0)
1207 return LastIndexOfAnyUnchecked (anyOf
, startIndex
, startIndex
+ 1);
1210 public int LastIndexOfAny (char [] anyOf
, int startIndex
, int count
)
1213 throw new ArgumentNullException ();
1215 if ((startIndex
< 0) || (startIndex
>= this.Length
))
1216 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1217 if ((count
< 0) || (count
> this.Length
))
1218 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1219 if (startIndex
- count
+ 1 < 0)
1220 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1222 if (this.length
== 0)
1225 return LastIndexOfAnyUnchecked (anyOf
, startIndex
, count
);
1228 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf
, int startIndex
, int count
)
1230 if (anyOf
.Length
== 1)
1231 return LastIndexOfUnchecked (anyOf
[0], startIndex
, count
);
1233 fixed (char* start
= this, testStart
= anyOf
) {
1234 char* ptr
= start
+ startIndex
;
1235 char* ptrEnd
= ptr
- count
;
1237 char* testEnd
= testStart
+ anyOf
.Length
;
1239 while (ptr
!= ptrEnd
) {
1241 while (test
!= testEnd
) {
1243 return (int)(ptr
- start
);
1252 // Following methods are culture-insensitive
1253 public int LastIndexOf (char value)
1255 if (this.length
== 0)
1258 return LastIndexOfUnchecked (value, this.length
- 1, this.length
);
1261 public int LastIndexOf (char value, int startIndex
)
1263 return LastIndexOf (value, startIndex
, startIndex
+ 1);
1266 public int LastIndexOf (char value, int startIndex
, int count
)
1268 if (startIndex
== 0 && this.length
== 0)
1271 // >= for char (> for string)
1272 if ((startIndex
< 0) || (startIndex
>= this.Length
))
1273 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1274 if ((count
< 0) || (count
> this.Length
))
1275 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1276 if (startIndex
- count
+ 1 < 0)
1277 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1279 return LastIndexOfUnchecked (value, startIndex
, count
);
1282 internal unsafe int LastIndexOfUnchecked (char value, int startIndex
, int count
)
1284 // It helps JIT compiler to optimize comparison
1285 int value_32
= (int)value;
1287 fixed (char* start
= &start_char
) {
1288 char* ptr
= start
+ startIndex
;
1289 char* end_ptr
= ptr
- (count
>> 3 << 3);
1291 while (ptr
!= end_ptr
) {
1292 if (*ptr
== value_32
)
1293 return (int)(ptr
- start
);
1294 if (ptr
[-1] == value_32
)
1295 return (int)(ptr
- start
) - 1;
1296 if (ptr
[-2] == value_32
)
1297 return (int)(ptr
- start
) - 2;
1298 if (ptr
[-3] == value_32
)
1299 return (int)(ptr
- start
) - 3;
1300 if (ptr
[-4] == value_32
)
1301 return (int)(ptr
- start
) - 4;
1302 if (ptr
[-5] == value_32
)
1303 return (int)(ptr
- start
) - 5;
1304 if (ptr
[-6] == value_32
)
1305 return (int)(ptr
- start
) - 6;
1306 if (ptr
[-7] == value_32
)
1307 return (int)(ptr
- start
) - 7;
1312 end_ptr
-= count
& 0x07;
1313 while (ptr
!= end_ptr
) {
1314 if (*ptr
== value_32
)
1315 return (int)(ptr
- start
);
1323 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex
, int count
)
1327 int end
= startIndex
- count
;
1328 char c
= Char
.ToUpperInvariant (value);
1329 fixed (char* s
= &start_char
) {
1330 for (int i
= startIndex
; i
> end
; i
--)
1331 if (Char
.ToUpperInvariant (s
[i
]) == c
)
1337 // Following methods are culture-sensitive
1338 public int LastIndexOf (String
value)
1340 if (this.length
== 0)
1341 // This overload does additional checking
1342 return LastIndexOf (value, 0, 0);
1344 return LastIndexOf (value, this.length
- 1, this.length
);
1347 public int LastIndexOf (String
value, int startIndex
)
1349 int max
= startIndex
;
1350 if (max
< this.Length
)
1352 return LastIndexOf (value, startIndex
, max
);
1355 public int LastIndexOf (String
value, int startIndex
, int count
)
1358 throw new ArgumentNullException ("value");
1360 // -1 > startIndex > for string (0 > startIndex >= for char)
1361 if ((startIndex
< -1) || (startIndex
> this.Length
))
1362 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1363 if ((count
< 0) || (count
> this.Length
))
1364 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1365 if (startIndex
- count
+ 1 < 0)
1366 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1368 if (value.Length
== 0)
1371 if (startIndex
== 0 && this.length
== 0)
1374 // This check is needed to match undocumented MS behaviour
1375 if (this.length
== 0 && value.length
> 0)
1381 if (startIndex
== this.Length
)
1383 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
);
1386 public bool Contains (String
value)
1388 return IndexOf (value) != -1;
1391 public static bool IsNullOrEmpty (String
value)
1393 return (value == null) || (value.Length
== 0);
1397 public string Normalize ()
1399 return Normalization
.Normalize (this, 0);
1402 public string Normalize (NormalizationForm normalizationForm
)
1404 switch (normalizationForm
) {
1406 return Normalization
.Normalize (this, 0);
1407 case NormalizationForm
.FormD
:
1408 return Normalization
.Normalize (this, 1);
1409 case NormalizationForm
.FormKC
:
1410 return Normalization
.Normalize (this, 2);
1411 case NormalizationForm
.FormKD
:
1412 return Normalization
.Normalize (this, 3);
1416 public bool IsNormalized ()
1418 return Normalization
.IsNormalized (this, 0);
1421 public bool IsNormalized (NormalizationForm normalizationForm
)
1423 switch (normalizationForm
) {
1425 return Normalization
.IsNormalized (this, 0);
1426 case NormalizationForm
.FormD
:
1427 return Normalization
.IsNormalized (this, 1);
1428 case NormalizationForm
.FormKC
:
1429 return Normalization
.IsNormalized (this, 2);
1430 case NormalizationForm
.FormKD
:
1431 return Normalization
.IsNormalized (this, 3);
1436 public string Remove (int startIndex
)
1439 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1440 if (startIndex
>= this.length
)
1441 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1443 return Remove (startIndex
, this.length
- startIndex
);
1446 public String
PadLeft (int totalWidth
)
1448 return PadLeft (totalWidth
, ' ');
1451 public unsafe String
PadLeft (int totalWidth
, char paddingChar
)
1453 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1456 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1458 if (totalWidth
< this.length
)
1461 String tmp
= InternalAllocateStr (totalWidth
);
1463 fixed (char* dest
= tmp
, src
= this) {
1464 char* padPos
= dest
;
1465 char* padTo
= dest
+ (totalWidth
- length
);
1466 while (padPos
!= padTo
)
1467 *padPos
++ = paddingChar
;
1469 CharCopy (padTo
, src
, length
);
1474 public String
PadRight (int totalWidth
)
1476 return PadRight (totalWidth
, ' ');
1479 public unsafe String
PadRight (int totalWidth
, char paddingChar
)
1481 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1484 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1486 if (totalWidth
< this.length
)
1488 if (totalWidth
== 0)
1489 return String
.Empty
;
1491 String tmp
= InternalAllocateStr (totalWidth
);
1493 fixed (char* dest
= tmp
, src
= this) {
1494 CharCopy (dest
, src
, length
);
1496 char* padPos
= dest
+ length
;
1497 char* padTo
= dest
+ totalWidth
;
1498 while (padPos
!= padTo
)
1499 *padPos
++ = paddingChar
;
1504 public bool StartsWith (String
value)
1507 throw new ArgumentNullException ("value");
1509 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1512 [ComVisible (false)]
1513 public bool StartsWith (string value, StringComparison comparisonType
)
1516 throw new ArgumentNullException ("value");
1518 switch (comparisonType
) {
1519 case StringComparison
.CurrentCulture
:
1520 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1521 case StringComparison
.CurrentCultureIgnoreCase
:
1522 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.IgnoreCase
);
1523 case StringComparison
.InvariantCulture
:
1524 return CultureInfo
.InvariantCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1525 case StringComparison
.InvariantCultureIgnoreCase
:
1526 return CultureInfo
.InvariantCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.IgnoreCase
);
1527 case StringComparison
.Ordinal
:
1528 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.Ordinal
);
1529 case StringComparison
.OrdinalIgnoreCase
:
1530 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.OrdinalIgnoreCase
);
1532 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
1533 throw new ArgumentException (msg
, "comparisonType");
1537 [ComVisible (false)]
1538 public bool EndsWith (string value, StringComparison comparisonType
)
1541 throw new ArgumentNullException ("value");
1543 switch (comparisonType
) {
1544 case StringComparison
.CurrentCulture
:
1545 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
1546 case StringComparison
.CurrentCultureIgnoreCase
:
1547 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.IgnoreCase
);
1548 case StringComparison
.InvariantCulture
:
1549 return CultureInfo
.InvariantCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
1550 case StringComparison
.InvariantCultureIgnoreCase
:
1551 return CultureInfo
.InvariantCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.IgnoreCase
);
1552 case StringComparison
.Ordinal
:
1553 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.Ordinal
);
1554 case StringComparison
.OrdinalIgnoreCase
:
1555 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.OrdinalIgnoreCase
);
1557 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
1558 throw new ArgumentException (msg
, "comparisonType");
1562 public bool StartsWith (String
value, bool ignoreCase
, CultureInfo culture
)
1564 if (culture
== null)
1565 culture
= CultureInfo
.CurrentCulture
;
1567 return culture
.CompareInfo
.IsPrefix (this, value, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
1570 // Following method is culture-insensitive
1571 public unsafe String
Replace (char oldChar
, char newChar
)
1573 if (this.length
== 0 || oldChar
== newChar
)
1576 int start_pos
= IndexOfUnchecked (oldChar
, 0, this.length
);
1577 if (start_pos
== -1)
1583 string tmp
= InternalAllocateStr (length
);
1584 fixed (char* dest
= tmp
, src
= &start_char
) {
1586 CharCopy (dest
, src
, start_pos
);
1588 char* end_ptr
= dest
+ length
;
1589 char* dest_ptr
= dest
+ start_pos
;
1590 char* src_ptr
= src
+ start_pos
;
1592 while (dest_ptr
!= end_ptr
) {
1593 if (*src_ptr
== oldChar
)
1594 *dest_ptr
= newChar
;
1596 *dest_ptr
= *src_ptr
;
1605 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1606 public String
Replace (String oldValue
, String newValue
)
1608 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1609 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1611 if (oldValue
== null)
1612 throw new ArgumentNullException ("oldValue");
1614 if (oldValue
.Length
== 0)
1615 throw new ArgumentException ("oldValue is the empty string.");
1617 if (this.Length
== 0)
1620 if (newValue
== null)
1621 newValue
= String
.Empty
;
1623 return ReplaceUnchecked (oldValue
, newValue
);
1626 private unsafe String
ReplaceUnchecked (String oldValue
, String newValue
)
1628 if (oldValue
.length
> length
)
1630 if (oldValue
.length
== 1 && newValue
.length
== 1) {
1631 return Replace (oldValue
[0], newValue
[0]);
1632 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1633 // because the length of the result would be this.length and length calculation unneccesary
1636 const int maxValue
= 200; // Allocate 800 byte maximum
1637 int* dat
= stackalloc int[maxValue
];
1638 fixed (char* source
= this, replace
= newValue
) {
1639 int i
= 0, count
= 0;
1640 while (i
< length
) {
1641 int found
= IndexOfOrdinalUnchecked (oldValue
, i
, length
- i
);
1645 if (count
< maxValue
)
1646 dat
[count
++] = found
;
1648 return ReplaceFallback (oldValue
, newValue
, maxValue
);
1650 i
= found
+ oldValue
.length
;
1654 int nlen
= this.length
+ ((newValue
.length
- oldValue
.length
) * count
);
1655 String tmp
= InternalAllocateStr (nlen
);
1657 int curPos
= 0, lastReadPos
= 0;
1658 fixed (char* dest
= tmp
) {
1659 for (int j
= 0; j
< count
; j
++) {
1660 int precopy
= dat
[j
] - lastReadPos
;
1661 CharCopy (dest
+ curPos
, source
+ lastReadPos
, precopy
);
1663 lastReadPos
= dat
[j
] + oldValue
.length
;
1664 CharCopy (dest
+ curPos
, replace
, newValue
.length
);
1665 curPos
+= newValue
.length
;
1667 CharCopy (dest
+ curPos
, source
+ lastReadPos
, length
- lastReadPos
);
1673 private String
ReplaceFallback (String oldValue
, String newValue
, int testedCount
)
1675 int lengthEstimate
= this.length
+ ((newValue
.length
- oldValue
.length
) * testedCount
);
1676 StringBuilder sb
= new StringBuilder (lengthEstimate
);
1677 for (int i
= 0; i
< length
;) {
1678 int found
= IndexOfOrdinalUnchecked (oldValue
, i
, length
- i
);
1680 sb
.Append (SubstringUnchecked (i
, length
- i
));
1683 sb
.Append (SubstringUnchecked (i
, found
- i
));
1684 sb
.Append (newValue
);
1685 i
= found
+ oldValue
.Length
;
1687 return sb
.ToString ();
1691 public unsafe String
Remove (int startIndex
, int count
)
1694 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1696 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1697 if (startIndex
> this.length
- count
)
1698 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1700 String tmp
= InternalAllocateStr (this.length
- count
);
1702 fixed (char *dest
= tmp
, src
= this) {
1704 CharCopy (dst
, src
, startIndex
);
1705 int skip
= startIndex
+ count
;
1707 CharCopy (dst
, src
+ skip
, length
- skip
);
1712 public String
ToLower ()
1714 return ToLower (CultureInfo
.CurrentCulture
);
1717 public String
ToLower (CultureInfo culture
)
1719 if (culture
== null)
1720 throw new ArgumentNullException ("culture");
1722 if (culture
.LCID
== 0x007F) // Invariant
1723 return ToLowerInvariant ();
1725 return culture
.TextInfo
.ToLower (this);
1728 public unsafe String
ToLowerInvariant ()
1731 return String
.Empty
;
1733 string tmp
= InternalAllocateStr (length
);
1734 fixed (char* source
= &start_char
, dest
= tmp
) {
1736 char* destPtr
= (char*)dest
;
1737 char* sourcePtr
= (char*)source
;
1739 for (int n
= 0; n
< length
; n
++) {
1740 *destPtr
= Char
.ToLowerInvariant (*sourcePtr
);
1748 public String
ToUpper ()
1750 return ToUpper (CultureInfo
.CurrentCulture
);
1753 public String
ToUpper (CultureInfo culture
)
1755 if (culture
== null)
1756 throw new ArgumentNullException ("culture");
1758 if (culture
.LCID
== 0x007F) // Invariant
1759 return ToUpperInvariant ();
1761 return culture
.TextInfo
.ToUpper (this);
1764 public unsafe String
ToUpperInvariant ()
1767 return String
.Empty
;
1769 string tmp
= InternalAllocateStr (length
);
1770 fixed (char* source
= &start_char
, dest
= tmp
) {
1772 char* destPtr
= (char*)dest
;
1773 char* sourcePtr
= (char*)source
;
1775 for (int n
= 0; n
< length
; n
++) {
1776 *destPtr
= Char
.ToUpperInvariant (*sourcePtr
);
1784 public override String
ToString ()
1789 public String
ToString (IFormatProvider provider
)
1794 public static String
Format (String format
, Object arg0
)
1796 return Format (null, format
, new Object
[] {arg0}
);
1799 public static String
Format (String format
, Object arg0
, Object arg1
)
1801 return Format (null, format
, new Object
[] {arg0, arg1}
);
1804 public static String
Format (String format
, Object arg0
, Object arg1
, Object arg2
)
1806 return Format (null, format
, new Object
[] {arg0, arg1, arg2}
);
1809 public static string Format (string format
, params object[] args
)
1811 return Format (null, format
, args
);
1814 public static string Format (IFormatProvider provider
, string format
, params object[] args
)
1816 StringBuilder b
= FormatHelper (null, provider
, format
, args
);
1817 return b
.ToString ();
1820 internal static StringBuilder
FormatHelper (StringBuilder result
, IFormatProvider provider
, string format
, params object[] args
)
1823 throw new ArgumentNullException ("format");
1825 throw new ArgumentNullException ("args");
1827 if (result
== null) {
1828 /* Try to approximate the size of result to avoid reallocations */
1832 for (i
= 0; i
< args
.Length
; ++i
) {
1833 string s
= args
[i
] as string;
1839 if (i
== args
.Length
)
1840 result
= new StringBuilder (len
+ format
.length
);
1842 result
= new StringBuilder ();
1847 while (ptr
< format
.length
) {
1848 char c
= format
[ptr
++];
1851 result
.Append (format
, start
, ptr
- start
- 1);
1853 // check for escaped open bracket
1855 if (format
[ptr
] == '{') {
1866 ParseFormatSpecifier (format
, ref ptr
, out n
, out width
, out left_align
, out arg_format
);
1867 if (n
>= args
.Length
)
1868 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1872 object arg
= args
[n
];
1875 ICustomFormatter formatter
= null;
1876 if (provider
!= null)
1877 formatter
= provider
.GetFormat (typeof (ICustomFormatter
))
1878 as ICustomFormatter
;
1881 else if (formatter
!= null)
1882 str
= formatter
.Format (arg_format
, arg
, provider
);
1883 else if (arg
is IFormattable
)
1884 str
= ((IFormattable
)arg
).ToString (arg_format
, provider
);
1886 str
= arg
.ToString ();
1888 // pad formatted string and append to result
1890 if (width
> str
.length
) {
1891 const char padchar
= ' ';
1892 int padlen
= width
- str
.length
;
1895 result
.Append (str
);
1896 result
.Append (padchar
, padlen
);
1899 result
.Append (padchar
, padlen
);
1900 result
.Append (str
);
1904 result
.Append (str
);
1908 else if (c
== '}' && ptr
< format
.length
&& format
[ptr
] == '}') {
1909 result
.Append (format
, start
, ptr
- start
- 1);
1912 else if (c
== '}') {
1913 throw new FormatException ("Input string was not in a correct format.");
1917 if (start
< format
.length
)
1918 result
.Append (format
, start
, format
.Length
- start
);
1923 public unsafe static String
Copy (String str
)
1926 throw new ArgumentNullException ("str");
1928 int length
= str
.length
;
1930 String tmp
= InternalAllocateStr (length
);
1932 fixed (char *dest
= tmp
, src
= str
) {
1933 CharCopy (dest
, src
, length
);
1939 public static String
Concat (Object arg0
)
1942 return String
.Empty
;
1944 return arg0
.ToString ();
1947 public static String
Concat (Object arg0
, Object arg1
)
1949 return Concat ((arg0
!= null) ? arg0
.ToString () : null, (arg1
!= null) ? arg1
.ToString () : null);
1952 public static String
Concat (Object arg0
, Object arg1
, Object arg2
)
1958 s1
= arg0
.ToString ();
1963 s2
= arg1
.ToString ();
1968 s3
= arg2
.ToString ();
1970 return Concat (s1
, s2
, s3
);
1973 [CLSCompliant(false)]
1974 public static String
Concat (Object arg0
, Object arg1
, Object arg2
,
1975 Object arg3
, __arglist
)
1977 string s1
, s2
, s3
, s4
;
1982 s1
= arg0
.ToString ();
1987 s2
= arg1
.ToString ();
1992 s3
= arg2
.ToString ();
1994 ArgIterator iter
= new ArgIterator (__arglist
);
1995 int argCount
= iter
.GetRemainingCount();
1997 StringBuilder sb
= new StringBuilder ();
1999 sb
.Append (arg3
.ToString ());
2001 for (int i
= 0; i
< argCount
; i
++) {
2002 TypedReference typedRef
= iter
.GetNextArg ();
2003 sb
.Append (TypedReference
.ToObject (typedRef
));
2006 s4
= sb
.ToString ();
2008 return Concat (s1
, s2
, s3
, s4
);
2011 public unsafe static String
Concat (String str0
, String str1
)
2013 if (str0
== null || str0
.Length
== 0) {
2014 if (str1
== null || str1
.Length
== 0)
2015 return String
.Empty
;
2019 if (str1
== null || str1
.Length
== 0)
2022 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
);
2024 fixed (char *dest
= tmp
, src
= str0
)
2025 CharCopy (dest
, src
, str0
.length
);
2026 fixed (char *dest
= tmp
, src
= str1
)
2027 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2032 public unsafe static String
Concat (String str0
, String str1
, String str2
)
2034 if (str0
== null || str0
.Length
== 0){
2035 if (str1
== null || str1
.Length
== 0){
2036 if (str2
== null || str2
.Length
== 0)
2037 return String
.Empty
;
2040 if (str2
== null || str2
.Length
== 0)
2043 str0
= String
.Empty
;
2045 if (str1
== null || str1
.Length
== 0){
2046 if (str2
== null || str2
.Length
== 0)
2049 str1
= String
.Empty
;
2051 if (str2
== null || str2
.Length
== 0)
2052 str2
= String
.Empty
;
2056 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
+ str2
.length
);
2058 if (str0
.Length
!= 0) {
2059 fixed (char *dest
= tmp
, src
= str0
) {
2060 CharCopy (dest
, src
, str0
.length
);
2063 if (str1
.Length
!= 0) {
2064 fixed (char *dest
= tmp
, src
= str1
) {
2065 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2068 if (str2
.Length
!= 0) {
2069 fixed (char *dest
= tmp
, src
= str2
) {
2070 CharCopy (dest
+ str0
.Length
+ str1
.Length
, src
, str2
.length
);
2077 public unsafe static String
Concat (String str0
, String str1
, String str2
, String str3
)
2079 if (str0
== null && str1
== null && str2
== null && str3
== null)
2080 return String
.Empty
;
2083 str0
= String
.Empty
;
2085 str1
= String
.Empty
;
2087 str2
= String
.Empty
;
2089 str3
= String
.Empty
;
2091 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
+ str2
.length
+ str3
.length
);
2093 if (str0
.Length
!= 0) {
2094 fixed (char *dest
= tmp
, src
= str0
) {
2095 CharCopy (dest
, src
, str0
.length
);
2098 if (str1
.Length
!= 0) {
2099 fixed (char *dest
= tmp
, src
= str1
) {
2100 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2103 if (str2
.Length
!= 0) {
2104 fixed (char *dest
= tmp
, src
= str2
) {
2105 CharCopy (dest
+ str0
.Length
+ str1
.Length
, src
, str2
.length
);
2108 if (str3
.Length
!= 0) {
2109 fixed (char *dest
= tmp
, src
= str3
) {
2110 CharCopy (dest
+ str0
.Length
+ str1
.Length
+ str2
.Length
, src
, str3
.length
);
2117 public static String
Concat (params Object
[] args
)
2120 throw new ArgumentNullException ("args");
2122 int argLen
= args
.Length
;
2124 return String
.Empty
;
2126 string [] strings
= new string [argLen
];
2128 for (int i
= 0; i
< argLen
; i
++) {
2129 if (args
[i
] != null) {
2130 strings
[i
] = args
[i
].ToString ();
2131 len
+= strings
[i
].length
;
2135 return ConcatInternal (strings
, len
);
2138 public static String
Concat (params String
[] values
)
2141 throw new ArgumentNullException ("values");
2144 for (int i
= 0; i
< values
.Length
; i
++) {
2145 String s
= values
[i
];
2150 return ConcatInternal (values
, len
);
2153 private static unsafe String
ConcatInternal (String
[] values
, int length
)
2156 return String
.Empty
;
2158 String tmp
= InternalAllocateStr (length
);
2160 fixed (char* dest
= tmp
) {
2162 for (int i
= 0; i
< values
.Length
; i
++) {
2163 String source
= values
[i
];
2164 if (source
!= null) {
2165 fixed (char* src
= source
) {
2166 CharCopy (dest
+ pos
, src
, source
.length
);
2168 pos
+= source
.Length
;
2175 public unsafe String
Insert (int startIndex
, String
value)
2178 throw new ArgumentNullException ("value");
2180 if (startIndex
< 0 || startIndex
> this.length
)
2181 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2183 if (value.Length
== 0)
2185 if (this.Length
== 0)
2187 String tmp
= InternalAllocateStr (this.length
+ value.length
);
2189 fixed (char *dest
= tmp
, src
= this, val
= value) {
2191 CharCopy (dst
, src
, startIndex
);
2193 CharCopy (dst
, val
, value.length
);
2194 dst
+= value.length
;
2195 CharCopy (dst
, src
+ startIndex
, length
- startIndex
);
2200 public static string Intern (string str
)
2203 throw new ArgumentNullException ("str");
2205 return InternalIntern (str
);
2208 public static string IsInterned (string str
)
2211 throw new ArgumentNullException ("str");
2213 return InternalIsInterned (str
);
2216 public static string Join (string separator
, string [] value)
2219 throw new ArgumentNullException ("value");
2220 if (separator
== null)
2221 separator
= String
.Empty
;
2223 return JoinUnchecked (separator
, value, 0, value.Length
);
2226 public static string Join (string separator
, string[] value, int startIndex
, int count
)
2229 throw new ArgumentNullException ("value");
2231 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2233 throw new ArgumentOutOfRangeException ("count", "< 0");
2234 if (startIndex
> value.Length
- count
)
2235 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2237 if (startIndex
== value.Length
)
2238 return String
.Empty
;
2239 if (separator
== null)
2240 separator
= String
.Empty
;
2242 return JoinUnchecked (separator
, value, startIndex
, count
);
2245 private static unsafe string JoinUnchecked (string separator
, string[] value, int startIndex
, int count
)
2247 // Unchecked parameters
2248 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2249 // separator and value must not be null
2252 int maxIndex
= startIndex
+ count
;
2253 // Precount the number of characters that the resulting string will have
2254 for (int i
= startIndex
; i
< maxIndex
; i
++) {
2255 String s
= value[i
];
2259 length
+= separator
.length
* (count
- 1);
2261 return String
.Empty
;
2263 String tmp
= InternalAllocateStr (length
);
2266 fixed (char* dest
= tmp
, sepsrc
= separator
) {
2267 // Copy each string from value except the last one and add a separator for each
2269 for (int i
= startIndex
; i
< maxIndex
; i
++) {
2270 String source
= value[i
];
2271 if (source
!= null) {
2272 if (source
.Length
> 0) {
2273 fixed (char* src
= source
)
2274 CharCopy (dest
+ pos
, src
, source
.Length
);
2275 pos
+= source
.Length
;
2278 if (separator
.Length
> 0) {
2279 CharCopy (dest
+ pos
, sepsrc
, separator
.Length
);
2280 pos
+= separator
.Length
;
2283 // Append last string that does not get an additional separator
2284 String sourceLast
= value[maxIndex
];
2285 if (sourceLast
!= null) {
2286 if (sourceLast
.Length
> 0) {
2287 fixed (char* src
= sourceLast
)
2288 CharCopy (dest
+ pos
, src
, sourceLast
.Length
);
2295 bool IConvertible
.ToBoolean (IFormatProvider provider
)
2297 return Convert
.ToBoolean (this, provider
);
2300 byte IConvertible
.ToByte (IFormatProvider provider
)
2302 return Convert
.ToByte (this, provider
);
2305 char IConvertible
.ToChar (IFormatProvider provider
)
2307 return Convert
.ToChar (this, provider
);
2310 DateTime IConvertible
.ToDateTime (IFormatProvider provider
)
2312 return Convert
.ToDateTime (this, provider
);
2315 decimal IConvertible
.ToDecimal (IFormatProvider provider
)
2317 return Convert
.ToDecimal (this, provider
);
2320 double IConvertible
.ToDouble (IFormatProvider provider
)
2322 return Convert
.ToDouble (this, provider
);
2325 short IConvertible
.ToInt16 (IFormatProvider provider
)
2327 return Convert
.ToInt16 (this, provider
);
2330 int IConvertible
.ToInt32 (IFormatProvider provider
)
2332 return Convert
.ToInt32 (this, provider
);
2335 long IConvertible
.ToInt64 (IFormatProvider provider
)
2337 return Convert
.ToInt64 (this, provider
);
2340 sbyte IConvertible
.ToSByte (IFormatProvider provider
)
2342 return Convert
.ToSByte (this, provider
);
2345 float IConvertible
.ToSingle (IFormatProvider provider
)
2347 return Convert
.ToSingle (this, provider
);
2350 object IConvertible
.ToType (Type targetType
, IFormatProvider provider
)
2352 if (targetType
== null)
2353 throw new ArgumentNullException ("type");
2354 return Convert
.ToType (this, targetType
, provider
, false);
2357 ushort IConvertible
.ToUInt16 (IFormatProvider provider
)
2359 return Convert
.ToUInt16 (this, provider
);
2362 uint IConvertible
.ToUInt32 (IFormatProvider provider
)
2364 return Convert
.ToUInt32 (this, provider
);
2367 ulong IConvertible
.ToUInt64 (IFormatProvider provider
)
2369 return Convert
.ToUInt64 (this, provider
);
2378 public CharEnumerator
GetEnumerator ()
2380 return new CharEnumerator (this);
2383 IEnumerator
<char> IEnumerable
<char>.GetEnumerator ()
2385 return new CharEnumerator (this);
2388 IEnumerator IEnumerable
.GetEnumerator ()
2390 return new CharEnumerator (this);
2393 private static void ParseFormatSpecifier (string str
, ref int ptr
, out int n
, out int width
,
2394 out bool left_align
, out string format
)
2396 // parses format specifier of form:
2402 // N = argument number (non-negative integer)
2404 n
= ParseDecimal (str
, ref ptr
);
2406 throw new FormatException ("Input string was not in a correct format.");
2408 // M = width (non-negative integer)
2410 if (str
[ptr
] == ',') {
2411 // White space between ',' and number or sign.
2413 while (Char
.IsWhiteSpace (str
[ptr
]))
2417 format
= str
.Substring (start
, ptr
- start
);
2419 left_align
= (str
[ptr
] == '-');
2423 width
= ParseDecimal (str
, ref ptr
);
2425 throw new FormatException ("Input string was not in a correct format.");
2430 format
= String
.Empty
;
2433 // F = argument format (string)
2435 if (str
[ptr
] == ':') {
2437 while (str
[ptr
] != '}')
2440 format
+= str
.Substring (start
, ptr
- start
);
2445 if (str
[ptr
++] != '}')
2446 throw new FormatException ("Input string was not in a correct format.");
2448 catch (IndexOutOfRangeException
) {
2449 throw new FormatException ("Input string was not in a correct format.");
2453 private static int ParseDecimal (string str
, ref int ptr
)
2459 if (c
< '0' || '9' < c
)
2462 n
= n
* 10 + c
- '0';
2473 internal unsafe void InternalSetChar (int idx
, char val
)
2475 if ((uint) idx
>= (uint) Length
)
2476 throw new ArgumentOutOfRangeException ("idx");
2478 fixed (char * pStr
= &start_char
)
2484 internal unsafe void InternalSetLength (int newLength
)
2486 if (newLength
> length
)
2487 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2489 // zero terminate, we can pass string objects directly via pinvoke
2490 // we also zero the rest of the string, since the new GC needs to be
2491 // able to handle the changing size (it will skip the 0 bytes).
2492 fixed (char * pStr
= &start_char
) {
2493 char *p
= pStr
+ newLength
;
2494 char *end
= pStr
+ length
;
2503 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
2504 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2505 public unsafe override int GetHashCode ()
2507 fixed (char * c
= this) {
2509 char * end
= cc
+ length
- 1;
2511 for (;cc
< end
; cc
+= 2) {
2512 h
= (h
<< 5) - h
+ *cc
;
2513 h
= (h
<< 5) - h
+ cc
[1];
2517 h
= (h
<< 5) - h
+ *cc
;
2522 #if MOONLIGHT || NET_4_0
2523 public static bool IsNullOrWhiteSpace (string value)
2527 foreach (char c
in value)
2528 if (!Char
.IsWhiteSpace (c
))
2534 public static string Concat (IEnumerable
<string> values
)
2537 throw new ArgumentNullException ("values");
2539 var stringList
= new List
<string> ();
2541 foreach (var v
in values
){
2547 return ConcatInternal (stringList
.ToArray (), len
);
2550 [ComVisibleAttribute(false)]
2551 public static string Concat
<T
> (IEnumerable
<T
> values
)
2554 throw new ArgumentNullException ("values");
2556 var stringList
= new List
<string> ();
2558 foreach (var v
in values
){
2559 string sr
= v
.ToString ();
2561 stringList
.Add (sr
);
2563 return ConcatInternal (stringList
.ToArray (), len
);
2566 [ComVisibleAttribute(false)]
2567 public static string Join (string separator
, IEnumerable
<string> values
)
2569 if (separator
== null)
2570 return Concat (values
);
2573 throw new ArgumentNullException ("values");
2575 var stringList
= new List
<string> ();
2576 foreach (var v
in values
)
2579 return JoinUnchecked (separator
, stringList
.ToArray (), 0, stringList
.Count
);
2582 [ComVisibleAttribute(false)]
2583 public static string Join (string separator
, params object [] values
)
2585 if (separator
== null)
2586 return Concat (values
);
2589 throw new ArgumentNullException ("values");
2591 var strCopy
= new string [values
.Length
];
2593 foreach (var v
in values
)
2594 strCopy
[i
++] = v
.ToString ();
2596 return JoinUnchecked (separator
, strCopy
, 0, strCopy
.Length
);
2599 [ComVisible (false)]
2600 public static string Join
<T
> (string separator
, IEnumerable
<T
> values
)
2602 if (separator
== null)
2603 return Concat
<T
> (values
);
2606 throw new ArgumentNullException ("values");
2608 var stringList
= new List
<string> ();
2609 foreach (var v
in values
)
2610 stringList
.Add (v
.ToString ());
2612 return JoinUnchecked (separator
, stringList
.ToArray (), 0, stringList
.Count
);
2615 internal unsafe int GetCaseInsensitiveHashCode ()
2617 fixed (char * c
= this) {
2619 char * end
= cc
+ length
- 1;
2621 for (;cc
< end
; cc
+= 2) {
2622 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (*cc
);
2623 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (cc
[1]);
2627 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (*cc
);
2632 // Certain constructors are redirected to CreateString methods with
2633 // matching argument list. The this pointer should not be used.
2634 #pragma warning disable 169
2635 private unsafe String
CreateString (sbyte* value)
2638 return String
.Empty
;
2640 byte* bytes
= (byte*) value;
2644 while (bytes
++ [0] != 0)
2646 } catch (NullReferenceException
) {
2647 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2648 } catch (AccessViolationException
) {
2649 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2652 return CreateString (value, 0, length
, null);
2655 private unsafe String
CreateString (sbyte* value, int startIndex
, int length
)
2657 return CreateString (value, startIndex
, length
, null);
2660 private unsafe String
CreateString (sbyte* value, int startIndex
, int length
, Encoding enc
)
2663 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2665 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2666 if (value + startIndex
< value)
2667 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2669 bool isDefaultEncoding
;
2671 if (isDefaultEncoding
= (enc
== null)) {
2673 throw new ArgumentNullException ("value");
2675 return String
.Empty
;
2677 enc
= Encoding
.Default
;
2680 byte [] bytes
= new byte [length
];
2683 fixed (byte* bytePtr
= bytes
)
2685 memcpy (bytePtr
, (byte*) (value + startIndex
), length
);
2686 } catch (NullReferenceException
) {
2687 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2688 } catch (AccessViolationException
) {
2689 if (!isDefaultEncoding
)
2692 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2695 // GetString () is called even when length == 0
2696 return enc
.GetString (bytes
);
2699 unsafe string CreateString (char *value)
2702 return string.Empty
;
2709 string result
= InternalAllocateStr (i
);
2712 fixed (char *dest
= result
) {
2713 CharCopy (dest
, value, i
);
2719 unsafe string CreateString (char *value, int startIndex
, int length
)
2722 return string.Empty
;
2724 throw new ArgumentNullException ("value");
2726 throw new ArgumentOutOfRangeException ("startIndex");
2728 throw new ArgumentOutOfRangeException ("length");
2730 string result
= InternalAllocateStr (length
);
2732 fixed (char *dest
= result
) {
2733 CharCopy (dest
, value + startIndex
, length
);
2738 unsafe string CreateString (char [] val
, int startIndex
, int length
)
2741 throw new ArgumentNullException ("value");
2743 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2745 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2746 if (startIndex
> val
.Length
- length
)
2747 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2749 return string.Empty
;
2751 string result
= InternalAllocateStr (length
);
2753 fixed (char *dest
= result
, src
= val
) {
2754 CharCopy (dest
, src
+ startIndex
, length
);
2759 unsafe string CreateString (char [] val
)
2762 return string.Empty
;
2763 if (val
.Length
== 0)
2764 return string.Empty
;
2765 string result
= InternalAllocateStr (val
.Length
);
2767 fixed (char *dest
= result
, src
= val
) {
2768 CharCopy (dest
, src
, val
.Length
);
2773 unsafe string CreateString (char c
, int count
)
2776 throw new ArgumentOutOfRangeException ("count");
2778 return string.Empty
;
2779 string result
= InternalAllocateStr (count
);
2780 fixed (char *dest
= result
) {
2782 char *end
= p
+ count
;
2790 #pragma warning restore 169
2792 /* helpers used by the runtime as well as above or eslewhere in corlib */
2793 internal static unsafe void memset (byte *dest
, int val
, int len
)
2804 val
= val
| (val
<< 8);
2805 val
= val
| (val
<< 16);
2808 int rest
= (int)dest
& 3;
2816 } while (rest
!= 0);
2819 ((int*)dest
) [0] = val
;
2820 ((int*)dest
) [1] = val
;
2821 ((int*)dest
) [2] = val
;
2822 ((int*)dest
) [3] = val
;
2827 ((int*)dest
) [0] = val
;
2839 static unsafe void memcpy4 (byte *dest
, byte *src
, int size
) {
2840 /*while (size >= 32) {
2841 // using long is better than int and slower than double
2842 // FIXME: enable this only on correct alignment or on platforms
2843 // that can tolerate unaligned reads/writes of doubles
2844 ((double*)dest) [0] = ((double*)src) [0];
2845 ((double*)dest) [1] = ((double*)src) [1];
2846 ((double*)dest) [2] = ((double*)src) [2];
2847 ((double*)dest) [3] = ((double*)src) [3];
2852 while (size
>= 16) {
2853 ((int*)dest
) [0] = ((int*)src
) [0];
2854 ((int*)dest
) [1] = ((int*)src
) [1];
2855 ((int*)dest
) [2] = ((int*)src
) [2];
2856 ((int*)dest
) [3] = ((int*)src
) [3];
2862 ((int*)dest
) [0] = ((int*)src
) [0];
2868 ((byte*)dest
) [0] = ((byte*)src
) [0];
2874 static unsafe void memcpy2 (byte *dest
, byte *src
, int size
) {
2876 ((short*)dest
) [0] = ((short*)src
) [0];
2877 ((short*)dest
) [1] = ((short*)src
) [1];
2878 ((short*)dest
) [2] = ((short*)src
) [2];
2879 ((short*)dest
) [3] = ((short*)src
) [3];
2885 ((short*)dest
) [0] = ((short*)src
) [0];
2891 ((byte*)dest
) [0] = ((byte*)src
) [0];
2893 static unsafe void memcpy1 (byte *dest
, byte *src
, int size
) {
2895 ((byte*)dest
) [0] = ((byte*)src
) [0];
2896 ((byte*)dest
) [1] = ((byte*)src
) [1];
2897 ((byte*)dest
) [2] = ((byte*)src
) [2];
2898 ((byte*)dest
) [3] = ((byte*)src
) [3];
2899 ((byte*)dest
) [4] = ((byte*)src
) [4];
2900 ((byte*)dest
) [5] = ((byte*)src
) [5];
2901 ((byte*)dest
) [6] = ((byte*)src
) [6];
2902 ((byte*)dest
) [7] = ((byte*)src
) [7];
2908 ((byte*)dest
) [0] = ((byte*)src
) [0];
2909 ((byte*)dest
) [1] = ((byte*)src
) [1];
2915 ((byte*)dest
) [0] = ((byte*)src
) [0];
2918 internal static unsafe void memcpy (byte *dest
, byte *src
, int size
) {
2919 // FIXME: if pointers are not aligned, try to align them
2920 // so a faster routine can be used. Handle the case where
2921 // the pointers can't be reduced to have the same alignment
2922 // (just ignore the issue on x86?)
2923 if ((((int)dest
| (int)src
) & 3) != 0) {
2924 if (((int)dest
& 1) != 0 && ((int)src
& 1) != 0 && size
>= 1) {
2930 if (((int)dest
& 2) != 0 && ((int)src
& 2) != 0 && size
>= 2) {
2931 ((short*)dest
) [0] = ((short*)src
) [0];
2936 if ((((int)dest
| (int)src
) & 1) != 0) {
2937 memcpy1 (dest
, src
, size
);
2940 if ((((int)dest
| (int)src
) & 2) != 0) {
2941 memcpy2 (dest
, src
, size
);
2945 memcpy4 (dest
, src
, size
);
2948 internal static unsafe void CharCopy (char *dest
, char *src
, int count
) {
2949 // Same rules as for memcpy, but with the premise that
2950 // chars can only be aligned to even addresses if their
2951 // enclosing types are correctly aligned
2952 if ((((int)(byte*)dest
| (int)(byte*)src
) & 3) != 0) {
2953 if (((int)(byte*)dest
& 2) != 0 && ((int)(byte*)src
& 2) != 0 && count
> 0) {
2954 ((short*)dest
) [0] = ((short*)src
) [0];
2959 if ((((int)(byte*)dest
| (int)(byte*)src
) & 2) != 0) {
2960 memcpy2 ((byte*)dest
, (byte*)src
, count
* 2);
2964 memcpy4 ((byte*)dest
, (byte*)src
, count
* 2);
2967 internal static unsafe void CharCopyReverse (char *dest
, char *src
, int count
)
2971 for (int i
= count
; i
> 0; i
--) {
2978 internal static unsafe void CharCopy (String target
, int targetIndex
, String source
, int sourceIndex
, int count
)
2980 fixed (char* dest
= target
, src
= source
)
2981 CharCopy (dest
+ targetIndex
, src
+ sourceIndex
, count
);
2984 internal static unsafe void CharCopy (String target
, int targetIndex
, Char
[] source
, int sourceIndex
, int count
)
2986 fixed (char* dest
= target
, src
= source
)
2987 CharCopy (dest
+ targetIndex
, src
+ sourceIndex
, count
);
2990 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2991 internal static unsafe void CharCopyReverse (String target
, int targetIndex
, String source
, int sourceIndex
, int count
)
2993 fixed (char* dest
= target
, src
= source
)
2994 CharCopyReverse (dest
+ targetIndex
, src
+ sourceIndex
, count
);
2997 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
2998 unsafe public extern String (char *value);
3000 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3001 unsafe public extern String (char *value, int startIndex
, int length
);
3003 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3004 unsafe public extern String (sbyte *value);
3006 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3007 unsafe public extern String (sbyte *value, int startIndex
, int length
);
3009 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3010 unsafe public extern String (sbyte *value, int startIndex
, int length
, Encoding enc
);
3012 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3013 public extern String (char [] value, int startIndex
, int length
);
3015 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3016 public extern String (char [] value);
3018 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3019 public extern String (char c
, int count
);
3021 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3022 private extern String
[] InternalSplit (char[] separator
, int count
, int options
);
3024 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3025 internal extern static String
InternalAllocateStr (int length
);
3027 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3028 private extern static string InternalIntern (string str
);
3030 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3031 private extern static string InternalIsInterned (string str
);
3033 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3034 private extern static int GetLOSLimit ();