// Jeffrey Stedfast (fejj@ximian.com)
// Dan Lewis (dihlewis@yahoo.co.uk)
// Sebastien Pouliot <sebastien@ximian.com>
+// Marek Safar (marek.safar@seznam.cz)
//
// (C) 2001 Ximian, Inc. http://www.ximian.com
-// Copyright (C) 2004 Novell (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.CompilerServices;
+
+#if NET_2_0
+using System.Collections.Generic;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
using Mono.Globalization.Unicode;
+#endif
namespace System
{
[Serializable]
- public sealed class String : IConvertible, ICloneable, IEnumerable,
#if NET_2_0
- IComparable, IComparable<String>
+ [ComVisible (true)]
+ public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
#else
- IComparable
+ public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
#endif
{
[NonSerialized] private int length;
if (len != b.length)
return false;
- if (len == 0)
- return true;
+ fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
+ char* s1_ptr = s1;
+ char* s2_ptr = s2;
- fixed (char * s1 = &a.start_char, s2 = &b.start_char) {
- // it must be one char, because 0 len is done above
- if (len < 2)
- return *s1 == *s2;
+ while (len >= 8) {
+ if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
+ ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
+ ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
+ ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
+ return false;
- // check by twos
- int * sint1 = (int *) s1, sint2 = (int *) s2;
- int n2 = len >> 1;
- do {
- if (*sint1++ != *sint2++)
+ s1_ptr += 8;
+ s2_ptr += 8;
+ len -= 8;
+ }
+
+ if (len >= 4) {
+ if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
+ ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
return false;
- } while (--n2 != 0);
- // nothing left
- if ((len & 1) == 0)
- return true;
+ s1_ptr += 4;
+ s2_ptr += 4;
+ len -= 4;
+ }
+
+ if (len > 1) {
+ if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
+ return false;
+
+ s1_ptr += 2;
+ s2_ptr += 2;
+ len -= 2;
+ }
- // check the last one
- return *(char *) sint1 == *(char *) sint2;
+ return len == 0 || *s1_ptr == *s2_ptr;
}
}
return !Equals (a, b);
}
+#if NET_2_0
+ [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
public override bool Equals (Object obj)
{
return Equals (this, obj as String);
}
+#if NET_2_0
+ [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
public bool Equals (String value)
{
return Equals (this, value);
if (count == 1)
return new String[1] { ToString() };
- return InternalSplit (separator, count);
+ return InternalSplit (separator, count, 0);
}
+#if NET_2_0
+ [ComVisible (false)]
+ [MonoDocumentationNote ("code should be moved to managed")]
+ public String[] Split (char[] separator, int count, StringSplitOptions options)
+ {
+ if (separator == null || separator.Length == 0)
+ return Split (WhiteChars, count, options);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+ if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+ throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
+
+ if (count == 0)
+ return new string [0];
+
+ return InternalSplit (separator, count, (int)options);
+ }
+
+ [ComVisible (false)]
+ public String[] Split (string[] separator, int count, StringSplitOptions options)
+ {
+ if (separator == null || separator.Length == 0)
+ return Split (WhiteChars, count, options);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
+ if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
+ throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
+
+ bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
+
+ if (count == 0 || (this == String.Empty && removeEmpty))
+ return new String [0];
+
+ ArrayList arr = new ArrayList ();
+
+ int pos = 0;
+ while (pos < this.Length) {
+ int matchIndex = -1;
+ int matchPos = Int32.MaxValue;
+
+ // Find the first position where any of the separators matches
+ for (int i = 0; i < separator.Length; ++i) {
+ string sep = separator [i];
+ if (sep == null || sep == String.Empty)
+ continue;
+
+ int match = IndexOf (sep, pos);
+ if (match > -1 && match < matchPos) {
+ matchIndex = i;
+ matchPos = match;
+ }
+ }
+
+ if (matchIndex == -1)
+ break;
+
+ if (matchPos == pos && removeEmpty) {
+ pos = matchPos + separator [matchIndex].Length;
+ }
+ else {
+ arr.Add (this.Substring (pos, matchPos - pos));
+
+ pos = matchPos + separator [matchIndex].Length;
+
+ if (arr.Count == count - 1) {
+ break;
+ }
+ }
+ }
+
+ if (arr.Count == 0)
+ return new String [] { this };
+ else {
+ if (removeEmpty && pos == this.Length) {
+ String[] res = new String [arr.Count];
+ arr.CopyTo (0, res, 0, arr.Count);
+
+ return res;
+ }
+ else {
+ String[] res = new String [arr.Count + 1];
+ arr.CopyTo (0, res, 0, arr.Count);
+ res [arr.Count] = this.Substring (pos);
+
+ return res;
+ }
+ }
+ }
+
+ [ComVisible (false)]
+ public String[] Split (char[] separator, StringSplitOptions options)
+ {
+ return Split (separator, Int32.MaxValue, options);
+ }
+
+ [ComVisible (false)]
+ public String[] Split (String[] separator, StringSplitOptions options)
+ {
+ return Split (separator, Int32.MaxValue, options);
+ }
+#endif
+
public unsafe String Substring (int startIndex)
{
+ if (startIndex == 0)
+ return this;
+
if (startIndex < 0 || startIndex > this.length)
throw new ArgumentOutOfRangeException ("startIndex");
}
private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
+#if NET_2_0
+ (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
+#endif
(char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
(char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
(char) 0x3000, (char) 0xFEFF };
+ public String Trim ()
+ {
+ return InternalTrim (WhiteChars, 0);
+ }
+
public String Trim (params char[] trimChars)
{
if (trimChars == null || trimChars.Length == 0)
return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
}
+#if NET_2_0
+ public static int Compare (string strA, string strB, StringComparison comparisonType)
+ {
+ switch (comparisonType) {
+ case StringComparison.CurrentCulture:
+ return Compare (strA, strB, false, CultureInfo.CurrentCulture);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return Compare (strA, strB, true, CultureInfo.CurrentCulture);
+ case StringComparison.InvariantCulture:
+ return Compare (strA, strB, false, CultureInfo.InvariantCulture);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return Compare (strA, strB, true, CultureInfo.InvariantCulture);
+ case StringComparison.Ordinal:
+ return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
+ default:
+ string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
+ throw new ArgumentException ("comparisonType", msg);
+ }
+ }
+ public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
+ {
+ switch (comparisonType) {
+ case StringComparison.CurrentCulture:
+ return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
+ case StringComparison.InvariantCulture:
+ return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
+ case StringComparison.Ordinal:
+ return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
+ default:
+ string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
+ throw new ArgumentException ("comparisonType", msg);
+ }
+ }
+
+ public static bool Equals (string a, string b, StringComparison comparisonType)
+ {
+ return String.Compare (a, b, comparisonType) == 0;
+ }
+
+ public bool Equals (string value, StringComparison comparisonType)
+ {
+ return String.Equals (this, value, comparisonType);
+ }
+#endif
public int CompareTo (Object value)
{
if (value == null)
return Compare (this, strB, false);
}
- public static int CompareOrdinal (String strA, String strB)
+ public static unsafe int CompareOrdinal (String strA, String strB)
{
if (strA == null) {
if (strB == null)
return 0;
else
return -1;
+ } else if (strB == null) {
+ return 1;
}
- else if (strB == null) {
+ fixed (char* aptr = strA, bptr = strB) {
+ char* ap = aptr;
+ char* end = ap + Math.Min (strA.Length, strB.Length);
+ char* bp = bptr;
+ while (ap < end) {
+ if (*ap != *bp)
+ return *ap - *bp;
+ ap++;
+ bp++;
+ }
+ return strA.Length - strB.Length;
+ }
+ }
+
+ internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
+ {
+ if (strA == null) {
+ if (strB == null)
+ return 0;
+ else
+ return -1;
+ } else if (strB == null) {
return 1;
}
* instantiate (and chances are it already has
* been.)
*/
- return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
+ return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
}
public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
+ {
+ return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
+ }
+
+ internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
{
if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
throw new ArgumentOutOfRangeException ();
len2 = strB.Length - indexB;
}
- return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
+ return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
}
public bool EndsWith (String value)
{
- if (value == null)
- throw new ArgumentNullException ("value");
-
- if (value.Length == 0)
- return true;
-
- if (value.length > this.length)
- return false;
+ return EndsWith (value, false, CultureInfo.CurrentCulture);
+ }
- return (0 == Compare (this, length - value.length, value, 0, value.length));
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
+ {
+ return (culture.CompareInfo.IsSuffix (this, value,
+ ignoreCase ? CompareOptions.IgnoreCase :
+ CompareOptions.None));
}
public int IndexOfAny (char [] anyOf)
{
if (anyOf == null)
throw new ArgumentNullException ("anyOf");
+ if (this.length == 0)
+ return -1;
return InternalIndexOfAny (anyOf, 0, this.length);
}
{
if (anyOf == null)
throw new ArgumentNullException ("anyOf");
- if (startIndex < 0 || startIndex >= this.length)
- throw new ArgumentOutOfRangeException ("sourceIndex");
+ if (startIndex < 0 || startIndex > this.length)
+ throw new ArgumentOutOfRangeException ("startIndex");
return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
}
return InternalIndexOfAny (anyOf, startIndex, count);
}
+ unsafe int InternalIndexOfAny (char[] anyOf, int startIndex, int count)
+ {
+ if (anyOf.Length == 0)
+ return -1;
+
+ if (anyOf.Length == 1)
+ return IndexOfImpl(anyOf[0], startIndex, count);
+
+ fixed (char* any = anyOf) {
+ int highest = *any;
+ int lowest = *any;
+
+ char* end_any_ptr = any + anyOf.Length;
+ char* any_ptr = any;
+ while (++any_ptr != end_any_ptr) {
+ if (*any_ptr > highest) {
+ highest = *any_ptr;
+ continue;
+ }
+
+ if (*any_ptr < lowest)
+ lowest = *any_ptr;
+ }
+
+ fixed (char* start = &start_char) {
+ char* ptr = start + startIndex;
+ char* end_ptr = ptr + count;
+
+ while (ptr != end_ptr) {
+ if (*ptr > highest || *ptr < lowest) {
+ ptr++;
+ continue;
+ }
+
+ if (*ptr == *any)
+ return (int)(ptr - start);
+
+ any_ptr = any;
+ while (++any_ptr != end_any_ptr) {
+ if (*ptr == *any_ptr)
+ return (int)(ptr - start);
+ }
+
+ ptr++;
+ }
+ }
+ }
+ return -1;
+ }
+
+
+#if NET_2_0
+ public int IndexOf (string value, StringComparison comparison)
+ {
+ return IndexOf (value, 0, this.Length, comparison);
+ }
+
+ public int IndexOf (string value, int startIndex, StringComparison comparison)
+ {
+ return IndexOf (value, startIndex, this.Length - startIndex, comparison);
+ }
+
+ public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
+ {
+ switch (comparison) {
+ case StringComparison.CurrentCulture:
+ return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+ case StringComparison.InvariantCulture:
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+ case StringComparison.Ordinal:
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
+ }
+ throw new SystemException ("INTERNAL ERROR: should not reach here ...");
+ }
+
+ public int LastIndexOf (string value, StringComparison comparison)
+ {
+ return LastIndexOf (value, value.Length - 1, value.Length, comparison);
+ }
+
+ public int LastIndexOf (string value, int startIndex, StringComparison comparison)
+ {
+ return LastIndexOf (value, startIndex, startIndex + 1, comparison);
+ }
+
+ public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
+ {
+ switch (comparison) {
+ case StringComparison.CurrentCulture:
+ return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+ case StringComparison.InvariantCulture:
+ return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
+ case StringComparison.Ordinal:
+ return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
+ }
+ throw new SystemException ("INTERNAL ERROR: should not reach here ...");
+ }
+#endif
+
public int IndexOf (char value)
{
- return IndexOf (value, 0, this.length);
+ if (this.length == 0)
+ return -1;
+
+ return IndexOfImpl (value, 0, this.length);
}
public int IndexOf (String value)
if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
return -1;
- for (int pos = startIndex; pos < startIndex + count; pos++) {
- if (this[pos] == value)
- return(pos);
+ return IndexOfImpl (value, startIndex, count);
+ }
+
+ unsafe int IndexOfImpl (char value, int startIndex, int count)
+ {
+ // It helps JIT compiler to optimize comparison
+ int value_32 = (int)value;
+
+ fixed (char* start = &start_char) {
+ char* ptr = start + startIndex;
+ char* end_ptr = ptr + (count >> 3 << 3);
+
+ while (ptr != end_ptr) {
+ if (*ptr == value_32)
+ return (int)(ptr - start);
+ if (ptr[1] == value_32)
+ return (int)(ptr - start + 1);
+ if (ptr[2] == value_32)
+ return (int)(ptr - start + 2);
+ if (ptr[3] == value_32)
+ return (int)(ptr - start + 3);
+ if (ptr[4] == value_32)
+ return (int)(ptr - start + 4);
+ if (ptr[5] == value_32)
+ return (int)(ptr - start + 5);
+ if (ptr[6] == value_32)
+ return (int)(ptr - start + 6);
+ if (ptr[7] == value_32)
+ return (int)(ptr - start + 7);
+
+ ptr += 8;
+ }
+
+ end_ptr += count & 0x07;
+ while (ptr != end_ptr) {
+ if (*ptr == value_32)
+ return (int)(ptr - start);
+
+ ptr++;
+ }
+ return -1;
}
- return -1;
}
/* But this one is culture-sensitive */
if (anyOf == null)
throw new ArgumentNullException ("anyOf");
- if (startIndex < 0 || startIndex > this.length)
+ if (startIndex < 0 || startIndex >= this.length)
throw new ArgumentOutOfRangeException ();
if (this.length == 0)
if (anyOf == null)
throw new ArgumentNullException ("anyOf");
- if ((startIndex < 0) || (startIndex > this.Length))
+ if ((startIndex < 0) || (startIndex >= this.Length))
throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
if ((count < 0) || (count > this.Length))
throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
{
if (this.length == 0)
return -1;
- else
- return LastIndexOf (value, this.length - 1, this.length);
+
+ return LastIndexOfImpl (value, this.length - 1, this.length);
}
public int LastIndexOf (String value)
if (startIndex - count + 1 < 0)
throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
- for(int pos = startIndex; pos > startIndex - count; pos--) {
- if (this [pos] == value)
- return pos;
+ return LastIndexOfImpl (value, startIndex, count);
+ }
+
+ /* This method is culture-insensitive */
+ unsafe int LastIndexOfImpl (char value, int startIndex, int count)
+ {
+ // It helps JIT compiler to optimize comparison
+ int value_32 = (int)value;
+
+ fixed (char* start = &start_char) {
+ char* ptr = start + startIndex;
+ char* end_ptr = ptr - (count >> 3 << 3);
+
+ while (ptr != end_ptr) {
+ if (*ptr == value_32)
+ return (int)(ptr - start);
+ if (ptr[-1] == value_32)
+ return (int)(ptr - start) - 1;
+ if (ptr[-2] == value_32)
+ return (int)(ptr - start) - 2;
+ if (ptr[-3] == value_32)
+ return (int)(ptr - start) - 3;
+ if (ptr[-4] == value_32)
+ return (int)(ptr - start) - 4;
+ if (ptr[-5] == value_32)
+ return (int)(ptr - start) - 5;
+ if (ptr[-6] == value_32)
+ return (int)(ptr - start) - 6;
+ if (ptr[-7] == value_32)
+ return (int)(ptr - start) - 7;
+
+ ptr -= 8;
+ }
+
+ end_ptr -= count & 0x07;
+ while (ptr != end_ptr) {
+ if (*ptr == value_32)
+ return (int)(ptr - start);
+
+ ptr--;
+ }
+ return -1;
}
- return -1;
}
/* But this one is culture-sensitive */
throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
if (value.Length == 0)
- return 0;
+ return startIndex;
if (startIndex == 0 && this.length == 0)
return -1;
if (this.length == 0 && value.length > 0)
return -1;
- if (value.length > startIndex)
- return -1;
-
if (count == 0)
return -1;
return IndexOf (value) != -1;
}
- public bool IsNormalized ()
+ public static bool IsNullOrEmpty (String value)
{
- return IsNormalized (NormalizationForm.FormC);
+ return (value == null) || (value.Length == 0);
}
- public bool IsNormalized (NormalizationForm f)
+ public string Normalize ()
{
- switch (f) {
- case NormalizationForm.FormKD:
- return Normalization.IsNormalized (this, 3);
- case NormalizationForm.FormKC:
- return Normalization.IsNormalized (this, 2);
- case NormalizationForm.FormD:
- return Normalization.IsNormalized (this, 1);
+ return Normalize (NormalizationForm.FormC);
+ }
+
+ public string Normalize (NormalizationForm form)
+ {
+ switch (form) {
default:
- return Normalization.IsNormalized (this, 0);
+ return Normalization.Normalize (this, 0);
+ case NormalizationForm.FormD:
+ return Normalization.Normalize (this, 1);
+ case NormalizationForm.FormKC:
+ return Normalization.Normalize (this, 2);
+ case NormalizationForm.FormKD:
+ return Normalization.Normalize (this, 3);
}
}
- public static bool IsNullOrEmpty (String value)
+ public bool IsNormalized ()
{
- return (value == null) || (value.Length == 0);
+ return IsNormalized (NormalizationForm.FormC);
+ }
+
+ public bool IsNormalized (NormalizationForm form)
+ {
+ switch (form) {
+ default:
+ return Normalization.IsNormalized (this, 0);
+ case NormalizationForm.FormD:
+ return Normalization.IsNormalized (this, 1);
+ case NormalizationForm.FormKC:
+ return Normalization.IsNormalized (this, 2);
+ case NormalizationForm.FormKD:
+ return Normalization.IsNormalized (this, 3);
+ }
}
public string Remove (int startIndex)
return Remove (startIndex, this.length - startIndex);
}
-
- public string Normalize ()
- {
- return Normalize (NormalizationForm.FormC);
- }
-
- public string Normalize (NormalizationForm f)
- {
- switch (f) {
- case NormalizationForm.FormKD:
- return Normalization.Normalize (this, 3);
- case NormalizationForm.FormKC:
- return Normalization.Normalize (this, 2);
- case NormalizationForm.FormD:
- return Normalization.Normalize (this, 1);
- default:
- return Normalization.Normalize (this, 0);
- }
- }
#endif
public String PadLeft (int totalWidth)
public bool StartsWith (String value)
{
- if (value == null)
- throw new ArgumentNullException ("value");
-
- if (value.Length == 0)
- return true;
+ return StartsWith (value, false, CultureInfo.CurrentCulture);
+ }
- if (this.length < value.length)
+#if NET_2_0
+ [ComVisible (false)]
+ public bool StartsWith (string value, StringComparison comparisonType)
+ {
+ switch (comparisonType) {
+ case StringComparison.CurrentCulture:
+ return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
+ case StringComparison.InvariantCulture:
+ return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
+ case StringComparison.Ordinal:
+ return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
+ default:
return false;
+ }
+ }
- return (0 == Compare (this, 0, value, 0 , value.length));
+ [ComVisible (false)]
+ public bool EndsWith (string value, StringComparison comparisonType)
+ {
+ switch (comparisonType) {
+ case StringComparison.CurrentCulture:
+ return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
+ case StringComparison.InvariantCulture:
+ return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
+ case StringComparison.InvariantCultureIgnoreCase:
+ return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
+ case StringComparison.Ordinal:
+ return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
+ case StringComparison.OrdinalIgnoreCase:
+ return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
+ default:
+ return false;
+ }
+ }
+
+#endif
+
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
+ {
+ if (culture == null)
+ culture = CultureInfo.CurrentCulture;
+
+ return (culture.CompareInfo.IsPrefix (this, value,
+ ignoreCase ? CompareOptions.IgnoreCase :
+ CompareOptions.None));
}
/* This method is culture insensitive */
- public String Replace (char oldChar, char newChar)
+ public unsafe String Replace (char oldChar, char newChar)
{
- return InternalReplace (oldChar, newChar);
+ if (this.length == 0 || oldChar == newChar)
+ return this;
+
+ int start_pos = IndexOfImpl (oldChar, 0, this.length);
+ if (start_pos == -1)
+ return this;
+
+ if (start_pos < 4)
+ start_pos = 0;
+
+ string tmp = InternalAllocateStr(length);
+ fixed (char* dest = tmp, src = &start_char) {
+ if (start_pos != 0)
+ memcpy((byte*)dest, (byte*)src, start_pos * 2);
+
+ char* end_ptr = dest + length;
+ char* dest_ptr = dest + start_pos;
+ char* src_ptr = src + start_pos;
+
+ while (dest_ptr != end_ptr) {
+ if (*src_ptr == oldChar)
+ *dest_ptr = newChar;
+ else
+ *dest_ptr = *src_ptr;
+
+ ++src_ptr;
+ ++dest_ptr;
+ }
+ }
+ return tmp;
}
/* This method is culture sensitive */
return culture.TextInfo.ToLower (this);
}
+#if NET_2_0
+ public unsafe String ToLowerInvariant ()
+#else
internal unsafe String ToLowerInvariant ()
+#endif
{
string tmp = InternalAllocateStr (length);
fixed (char* source = &start_char, dest = tmp) {
return culture.TextInfo.ToUpper (this);
}
+#if NET_2_0
+ public unsafe String ToUpperInvariant ()
+#else
internal unsafe String ToUpperInvariant ()
+#endif
{
string tmp = InternalAllocateStr (length);
fixed (char* source = &start_char, dest = tmp) {
return this;
}
- public String Trim ()
- {
- return Trim (null);
- }
-
public static String Format (String format, Object arg0)
{
return Format (null, format, new Object[] {arg0});
object arg = args[n];
string str;
+ ICustomFormatter formatter = null;
+ if (provider != null)
+ formatter = provider.GetFormat (typeof (ICustomFormatter))
+ as ICustomFormatter;
if (arg == null)
- str = "";
+ str = String.Empty;
+ else if (formatter != null)
+ str = formatter.Format (arg_format, arg, provider);
else if (arg is IFormattable)
str = ((IFormattable)arg).ToString (arg_format, provider);
else
// pad formatted string and append to result
if (width > str.length) {
- string pad = new String (' ', width - str.length);
+ const char padchar = ' ';
+ int padlen = width - str.length;
if (left_align) {
result.Append (str);
- result.Append (pad);
+ result.Append (padchar, padlen);
}
else {
- result.Append (pad);
+ result.Append (padchar, padlen);
result.Append (str);
}
}
}
if (start < format.length)
- result.Append (format.Substring (start));
+ result.Append (format, start, format.Length - start);
}
public unsafe static String Copy (String str)
public unsafe static String Concat (String s1, String s2)
{
- if (s1 == null) {
- if (s2 == null)
+ if (s1 == null || s1.Length == 0) {
+ if (s2 == null || s2.Length == 0)
return String.Empty;
return s2;
}
- if (s2 == null)
+ if (s2 == null || s2.Length == 0)
return s1;
String tmp = InternalAllocateStr (s1.length + s2.length);
public unsafe static String Concat (String s1, String s2, String s3)
{
- if (s1 == null){
- if (s2 == null){
- if (s3 == null)
+ if (s1 == null || s1.Length == 0){
+ if (s2 == null || s2.Length == 0){
+ if (s3 == null || s3.Length == 0)
return String.Empty;
return s3;
} else {
- if (s3 == null)
+ if (s3 == null || s3.Length == 0)
return s2;
}
s1 = String.Empty;
} else {
- if (s2 == null){
- if (s3 == null)
+ if (s2 == null || s2.Length == 0){
+ if (s3 == null || s3.Length == 0)
return s1;
else
s2 = String.Empty;
} else {
- if (s3 == null)
+ if (s3 == null || s3.Length == 0)
s3 = String.Empty;
}
}
return Convert.ToSingle (this, provider);
}
- string IConvertible.ToString (IFormatProvider format)
- {
- return this;
- }
-
object IConvertible.ToType (Type conversionType, IFormatProvider provider)
{
return Convert.ToType (this, conversionType, provider);
return Convert.ToUInt64 (this, provider);
}
- TypeCode IConvertible.GetTypeCode ()
- {
- return TypeCode.String;
- }
-
public int Length {
get {
return length;
return new CharEnumerator (this);
}
+#if NET_2_0
+ IEnumerator<char> IEnumerable<char>.GetEnumerator ()
+ {
+ return GetEnumerator ();
+ }
+#endif
+
IEnumerator IEnumerable.GetEnumerator ()
{
return new CharEnumerator (this);
if (str[ptr] == ',') {
// White space between ',' and number or sign.
- int start = ++ptr;
+ ++ptr;
while (Char.IsWhiteSpace (str [ptr]))
++ptr;
+ int start = ptr;
format = str.Substring (start, ptr - start);
else {
width = 0;
left_align = false;
- format = "";
+ format = String.Empty;
}
// F = argument format (string)
if (newLength > length)
throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
- length = newLength;
-
// zero terminate, we can pass string objects directly via pinvoke
+ // we also zero the rest of the string, since the new GC needs to be
+ // able to handle the changing size (it will skip the 0 bytes).
fixed (char * pStr = &start_char) {
- pStr [length] = '\0';
+ char *p = pStr + newLength;
+ char *end = pStr + length;
+ while (p < end) {
+ p [0] = '\0';
+ p++;
+ }
}
+ length = newLength;
}
+#if NET_2_0
+ [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
+#endif
+ // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
public unsafe override int GetHashCode ()
{
fixed (char * c = this) {
}
}
+ internal unsafe int GetCaseInsensitiveHashCode ()
+ {
+ TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
+ fixed (char * c = this) {
+ char * cc = c;
+ char * end = cc + length - 1;
+ int h = 0;
+ for (;cc < end; cc += 2) {
+ h = (h << 5) - h + ti.ToUpper (*cc);
+ h = (h << 5) - h + ti.ToUpper (cc [1]);
+ }
+ ++end;
+ if (cc < end)
+ h = (h << 5) - h + ti.ToUpper (*cc);
+ return h;
+ }
+ }
+
+ // Certain constructors are redirected to CreateString methods with
+ // matching argument list. The this pointer should not be used.
+
+ private unsafe String CreateString (sbyte* value)
+ {
+ if (value == null)
+ return String.Empty;
+
+ byte* bytes = (byte*) value;
+ int length = 0;
+
+ try {
+ while (bytes++ [0] != 0)
+ length++;
+ } catch (NullReferenceException) {
+ throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
+#if NET_2_0
+ } catch (AccessViolationException) {
+ throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
+#endif
+ }
+
+ return CreateString (value, 0, length, null);
+ }
+
+ private unsafe String CreateString (sbyte* value, int startIndex, int length)
+ {
+ return CreateString (value, startIndex, length, null);
+ }
+
+ private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
+ {
+ if (length < 0)
+ throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
+ if (value + startIndex < value)
+ throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
+
+ bool isDefaultEncoding;
+
+ if (isDefaultEncoding = (enc == null)) {
+#if NET_2_0
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ if (length == 0)
+#else
+ if (value == null || length == 0)
+#endif
+ return String.Empty;
+
+ enc = Encoding.Default;
+ }
+
+ byte [] bytes = new byte [length];
+
+ if (length != 0)
+ fixed (byte* bytePtr = bytes)
+ try {
+ memcpy (bytePtr, (byte*) (value + startIndex), length);
+ } catch (NullReferenceException) {
+#if !NET_2_0
+ if (!isDefaultEncoding)
+ throw;
+#endif
+
+ throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
+#if NET_2_0
+ } catch (AccessViolationException) {
+ if (!isDefaultEncoding)
+ throw;
+
+ throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
+#endif
+ }
+
+ // GetString () is called even when length == 0
+ return enc.GetString (bytes);
+ }
+
+ unsafe string CreateString (char *value)
+ {
+ if (value == null)
+ return string.Empty;
+ char *p = value;
+ int i = 0;
+ while (*p != 0) {
+ ++i;
+ ++p;
+ }
+ string result = InternalAllocateStr (i);
+
+ if (i != 0) {
+ fixed (char *dest = result) {
+ memcpy ((byte*)dest, (byte*)value, i * 2);
+ }
+ }
+ return result;
+ }
+
+ unsafe string CreateString (char *value, int startIndex, int length)
+ {
+ if (length == 0)
+ return string.Empty;
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException ("startIndex");
+ if (length < 0)
+ throw new ArgumentOutOfRangeException ("length");
+
+ string result = InternalAllocateStr (length);
+
+ fixed (char *dest = result) {
+ memcpy ((byte*)dest, (byte*)(value + startIndex), length * 2);
+ }
+ return result;
+ }
+
+ unsafe string CreateString (char [] val, int startIndex, int length)
+ {
+ if (val == null)
+ throw new ArgumentNullException ("val");
+ if (startIndex < 0)
+ throw new ArgumentOutOfRangeException ("startIndex");
+ if (length < 0)
+ throw new ArgumentOutOfRangeException ("length");
+ if (startIndex > val.Length - length)
+ throw new ArgumentOutOfRangeException ("Out of range");
+ if (length == 0)
+ return string.Empty;
+
+ string result = InternalAllocateStr (length);
+
+ fixed (char *dest = result, src = val) {
+ memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
+ }
+ return result;
+ }
+
+ unsafe string CreateString (char [] val)
+ {
+ if (val == null)
+ return string.Empty;
+ if (val.Length == 0)
+ return string.Empty;
+ string result = InternalAllocateStr (val.Length);
+
+ fixed (char *dest = result, src = val) {
+ memcpy ((byte*)dest, (byte*)src, val.Length * 2);
+ }
+ return result;
+ }
+
+ unsafe string CreateString (char c, int count)
+ {
+ if (count < 0)
+ throw new ArgumentOutOfRangeException ("count");
+ if (count == 0)
+ return string.Empty;
+ string result = InternalAllocateStr (count);
+ fixed (char *dest = result) {
+ char *p = dest;
+ char *end = p + count;
+ while (p < end) {
+ *p = c;
+ p++;
+ }
+ }
+ return result;
+ }
+
/* helpers used by the runtime as well as above or eslewhere in corlib */
internal static unsafe void memset (byte *dest, int val, int len)
{
}
}
- internal static unsafe void memcpy4 (byte *dest, byte *src, int size) {
+ static unsafe void memcpy4 (byte *dest, byte *src, int size) {
/*while (size >= 32) {
// using long is better than int and slower than double
// FIXME: enable this only on correct alignment or on platforms
if (size > 0)
((byte*)dest) [0] = ((byte*)src) [0];
}
- static unsafe void memcpy (byte *dest, byte *src, int size) {
+
+ internal static unsafe void memcpy (byte *dest, byte *src, int size) {
// FIXME: if pointers are not aligned, try to align them
// so a faster routine can be used. Handle the case where
// the pointers can't be reduced to have the same alignment
[MethodImplAttribute (MethodImplOptions.InternalCall)]
private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
- [MethodImplAttribute (MethodImplOptions.InternalCall)]
- private extern String InternalReplace (char oldChar, char newChar);
-
[MethodImplAttribute (MethodImplOptions.InternalCall)]
private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
[MethodImplAttribute (MethodImplOptions.InternalCall)]
- private extern String[] InternalSplit (char[] separator, int count);
+ private extern String[] InternalSplit (char[] separator, int count, int options);
[MethodImplAttribute (MethodImplOptions.InternalCall)]
private extern String InternalTrim (char[] chars, int typ);
- [MethodImplAttribute (MethodImplOptions.InternalCall)]
- private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
-
[MethodImplAttribute (MethodImplOptions.InternalCall)]
private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);