-// -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
//
// System.String.cs
//
// Authors:
+// Patrik Torstensson
// Jeffrey Stedfast (fejj@ximian.com)
// Dan Lewis (dihlewis@yahoo.co.uk)
//
// (C) 2001 Ximian, Inc. http://www.ximian.com
//
-// FIXME: from what I gather from msdn, when a function is to return an empty string
-// we should be returning this.Empty - some methods do this and others don't.
-
-// FIXME: I didn't realise until later that `string' has a .Length method and so
-// I am missing some proper bounds-checking in some methods. Find these
-// instances and throw the ArgumentOutOfBoundsException at the programmer.
-// I like pelting programmers with ArgumentOutOfBoundsException's :-)
-
-// FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
-// need to be made unicode aware.
-
-// FIXME: when you have a char carr[], does carr.Length include the terminating null char?
-
using System;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.CompilerServices;
-namespace System {
+namespace System
+{
+ [Serializable]
+ public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable
+ {
+ [NonSerialized] private int length;
+ [NonSerialized] private char start_char;
- //[DefaultMemberName("Chars")]
- public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
- public static readonly string Empty = "";
- private char[] c_str;
- private int length;
+ private const int COMPARE_CASE = 0;
+ private const int COMPARE_INCASE = 1;
+ private const int COMPARE_ORDINAL = 2;
- // Constructors
+ public static readonly String Empty = "";
- internal String (int storage)
- {
- if (storage < 0)
- throw new ArgumentOutOfRangeException ();
- length = storage;
- c_str = new char [storage];
- }
-
- [CLSCompliant(false)]
- unsafe public String (char *value)
+ public static unsafe bool Equals (string a, string b)
{
- int i;
+ if ((a as object) == (b as object))
+ return true;
- // FIXME: can I do value.Length here?
- if (value == null) {
- this.length = 0;
- } else {
- for (i = 0; *(value + i) != '\0'; i++);
- this.length = i;
- }
+ if (a == null || b == null)
+ return false;
+
+ int len = a.length;
+
+ if (len != b.length)
+ return false;
+
+ if (len == 0)
+ return true;
+
+ 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;
+
+ // check by twos
+ int * sint1 = (int *) s1, sint2 = (int *) s2;
+ int n2 = len >> 1;
+ do {
+ if (*sint1++ != *sint2++)
+ return false;
+ } while (--n2 != 0);
+
+ // nothing left
+ if ((len & 1) == 0)
+ return true;
- this.c_str = new char [this.length + 1];
- for (i = 0; i < this.length; i++)
- this.c_str[i] = *(value + i);
+ // check the last one
+ return *(char *) sint1 == *(char *) sint2;
+ }
}
- public String (char[] value)
+ public static bool operator == (String a, String b)
{
- int i;
+ return Equals (a, b);
+ }
- // FIXME: value.Length includes the terminating null char?
- this.length = value != null ? strlen (value): 0;
- this.c_str = new char [this.length + 1];
- for (i = 0; i < this.length; i++)
- this.c_str[i] = value[i];
+ public static bool operator != (String a, String b)
+ {
+ return !Equals (a, b);
}
- [CLSCompliant(false)]
- unsafe public String (sbyte *value)
+ public override bool Equals (Object obj)
{
- // FIXME: consider unicode?
- int i;
+ return Equals (this, obj as String);
+ }
- // FIXME: can I do value.Length here? */
- if (value == null) {
- this.length = 0;
- } else {
- for (i = 0; *(value + i) != '\0'; i++);
- this.length = i;
- }
+ public bool Equals (String value)
+ {
+ return Equals (this, value);
+ }
- this.c_str = new char [this.length + 1];
- for (i = 0; i < this.length; i++)
- this.c_str[i] = (char) *(value + i);
+ [IndexerName ("Chars")]
+ public extern char this [int index] {
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ get;
}
- public String (char c, int count)
+ public Object Clone ()
{
- int i;
+ return this;
+ }
- this.length = count;
- this.c_str = new char [count + 1];
- for (i = 0; i < count; i++)
- this.c_str[i] = c;
+ public TypeCode GetTypeCode ()
+ {
+ return TypeCode.String;
}
- [CLSCompliant(false)]
- unsafe public String (char *value, int startIndex, int length)
+ public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
{
- int i;
+ // LAMESPEC: should I null-terminate?
+ if (destination == null)
+ throw new ArgumentNullException ("destination");
- if (value == null && startIndex != 0 && length != 0)
- throw new ArgumentNullException ();
+ if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
+ throw new ArgumentOutOfRangeException ();
+
+ if (sourceIndex + count > Length)
+ throw new ArgumentOutOfRangeException ();
- if (startIndex < 0 || length < 0)
+ if (destinationIndex + count > destination.Length)
throw new ArgumentOutOfRangeException ();
- this.length = length;
- this.c_str = new char [length + 1];
- for (i = 0; i < length; i++)
- this.c_str[i] = *(value + startIndex + i);
+ InternalCopyTo (sourceIndex, destination, destinationIndex, count);
}
- public String (char[] value, int startIndex, int length)
+ public char[] ToCharArray ()
{
- int i;
+ return ToCharArray (0, length);
+ }
- if (value == null && startIndex != 0 && length != 0)
- throw new ArgumentNullException ();
+ public char[] ToCharArray (int startIndex, int length)
+ {
+ if (startIndex < 0 || length < 0 || startIndex + length > this.length)
+ throw new ArgumentOutOfRangeException ();
- if (startIndex < 0 || length < 0)
- throw new ArgumentOutOfRangeException ();
+ char [] tmp = new char[length];
- this.length = length;
- this.c_str = new char [length + 1];
- for (i = 0; i < length; i++)
- this.c_str[i] = value[startIndex + i];
+ InternalCopyTo (startIndex, tmp, 0, length);
+
+ return tmp;
}
- [CLSCompliant(false)]
- unsafe public String (sbyte *value, int startIndex, int length)
+ public String [] Split (params char [] separator)
{
- // FIXME: consider unicode?
- int i;
+ return Split (separator, Int32.MaxValue);
+ }
- if (value == null && startIndex != 0 && length != 0)
- throw new ArgumentNullException ();
+ public String[] Split (char[] separator, int count)
+ {
+ if (separator == null || separator.Length == 0)
+ separator = WhiteChars;
- if (startIndex < 0 || length < 0)
+ if (count < 0)
throw new ArgumentOutOfRangeException ();
- this.length = length;
- this.c_str = new char [length + 1];
- for (i = 0; i < length; i++)
- this.c_str[i] = (char) *(value + startIndex + i);
+ if (count == 0)
+ return new String[0];
+
+ if (count == 1)
+ return new String[1] { ToString() };
+
+ return InternalSplit (separator, count);
}
- [CLSCompliant(false)][MonoTODO]
- unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
+ public String Substring (int startIndex)
{
- // FIXME: implement me
+ if (startIndex < 0 || startIndex > this.length)
+ throw new ArgumentOutOfRangeException ("startIndex");
+
+ string tmp = InternalAllocateStr (this.length - startIndex);
+ InternalStrcpy (tmp, 0, this, startIndex, length - startIndex);
+
+ return tmp;
}
- ~String ()
+ public String Substring (int startIndex, int length)
{
- // FIXME: is there anything we need to do here?
- /*base.Finalize ();*/
- }
+ if (length < 0 || startIndex < 0 || startIndex + length > this.length)
+ throw new ArgumentOutOfRangeException ();
- // Properties
- public int Length {
- get {
- return this.length;
- }
+ if (length == 0)
+ return String.Empty;
+
+ string tmp = InternalAllocateStr (length);
+ InternalStrcpy (tmp, 0, this, startIndex, length);
+
+ return tmp;
+ }
+
+ private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
+ (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 (params char[] trimChars)
+ {
+ if (trimChars == null || trimChars.Length == 0)
+ trimChars = WhiteChars;
+
+ return InternalTrim (trimChars, 0);
}
- [IndexerName("Chars")]
- public char this [int index] {
- get {
- if (index >= this.length)
- throw new ArgumentOutOfRangeException ();
+ public String TrimStart (params char[] trimChars)
+ {
+ if (trimChars == null || trimChars.Length == 0)
+ trimChars = WhiteChars;
- return this.c_str[index];
- }
+ return InternalTrim (trimChars, 1);
}
- // Private helper methods
- private static int strlen (char[] str)
+ public String TrimEnd (params char[] trimChars)
{
- // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
- return str.Length;
+ if (trimChars == null || trimChars.Length == 0)
+ trimChars = WhiteChars;
+
+ return InternalTrim (trimChars, 2);
}
- [MonoTODO]
- private static char tolowerordinal (char c)
+ public static int Compare (String strA, String strB)
{
- // FIXME: implement me
- return c;
+ return Compare (strA, strB, false, CultureInfo.CurrentCulture);
}
- private static bool is_lwsp (char c)
+ public static int Compare (String strA, String strB, bool ignoreCase)
{
- /* this comes from the msdn docs for String.Trim() */
- if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
- (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
- return true;
- else
- return false;
+ return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
}
- private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
+ public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
{
- /* (hopefully) Unicode-safe Boyer-Moore implementation */
- int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
- int h, n, he, ne, hc, nc, i;
+ if (culture == null)
+ throw new ArgumentNullException ("culture");
- if (haystack == null || needle == null)
- throw new ArgumentNullException ();
+ if (strA == null) {
+ if (strB == null)
+ return 0;
+ else
+ return -1;
- /* if the search buffer is shorter than the pattern buffer, we can't match */
- if (count < needle.length)
- return -1;
+ }
+ else if (strB == null) {
+ return 1;
+ }
- /* return an instant match if the pattern is 0-length */
- if (needle.length == 0)
- return startIndex;
+ CompareOptions compopts;
- /* set a pointer at the end of each string */
- ne = needle.length - 1; /* position of char before '\0' */
- he = startIndex + count; /* position of last valid char */
-
- /* init the skip table with the pattern length */
- nc = needle.length;
- for (i = 0; i < 65536; i++)
- skiptable[i] = nc;
-
- /* set the skip value for the chars that *do* appear in the
- * pattern buffer (needle) to the distance from the index to
- * the end of the pattern buffer. */
- for (nc = 0; nc < ne; nc++)
- skiptable[(int) needle[nc]] = ne - nc;
-
- h = startIndex;
- while (count >= needle.length) {
- hc = h + needle.length - 1; /* set the haystack compare pointer */
- nc = ne; /* set the needle compare pointer */
-
- /* work our way backwards until they don't match */
- for (i = 0; nc > 0; nc--, hc--, i++)
- if (needle[nc] != haystack[hc])
- break;
-
- if (needle[nc] != haystack[hc]) {
- n = skiptable[(int) haystack[hc]] - i;
- h += n;
- count -= n;
- } else
- return h;
- }
+ if (ignoreCase)
+ compopts = CompareOptions.IgnoreCase;
+ else
+ compopts = CompareOptions.None;
- return -1;
+ return culture.CompareInfo.Compare (strA, strB, compopts);
}
- // Methods
- [MonoTODO]
- public object Clone ()
+ public static int Compare (String strA, int indexA, String strB, int indexB, int length)
{
- // FIXME: implement me
- return null;
+ return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
}
- internal enum _StringCompareMode {
- CompareDirect,
- CompareCaseInsensitive,
- CompareOrdinal
- };
-
- internal static int _CompareGetLength (string strA, string strB)
+ public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
{
- if ((strA == null) || (strB == null))
- return 0;
- else
- return Math.Max (strA.Length, strB.Length);
+ return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
}
-
- internal static int _CompareChar (char chrA, char chrB, CultureInfo culture,
- _StringCompareMode mode)
+
+ public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
{
- int result = 0;
+ if (culture == null)
+ throw new ArgumentNullException ("culture");
- switch (mode) {
- case _StringCompareMode.CompareDirect:
- // FIXME: We should do a culture based comparision here,
- // but for the moment let's do it by hand.
- // In the microsoft runtime, uppercase letters
- // sort after lowercase letters in the default
- // culture.
- if (Char.IsUpper (chrA) && Char.IsLower (chrB))
- return 1;
- else if (Char.IsLower (chrA) && Char.IsUpper (chrB))
- return -1;
- result = (int) (chrA - chrB);
- break;
- case _StringCompareMode.CompareCaseInsensitive:
- result = (int) (Char.ToLower (chrA) - Char.ToLower (chrB));
- break;
- case _StringCompareMode.CompareOrdinal:
- result = (int) (tolowerordinal (chrA) - tolowerordinal (chrB));
- break;
- }
+ if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
+ throw new ArgumentOutOfRangeException ();
- if (result == 0)
+ if (length == 0)
+ return 0;
+
+ if (strA == null) {
+ if (strB == null) {
return 0;
- else if (result < 0)
+ } else {
return -1;
- else
+ }
+ }
+ else if (strB == null) {
return 1;
+ }
+
+ CompareOptions compopts;
+
+ if (ignoreCase)
+ compopts = CompareOptions.IgnoreCase;
+ else
+ compopts = CompareOptions.None;
+
+ /* Need to cap the requested length to the
+ * length of the string, because
+ * CompareInfo.Compare will insist that length
+ * <= (string.Length - offset)
+ */
+ int len1 = length;
+ int len2 = length;
+
+ if (length > (strA.Length - indexA)) {
+ len1 = strA.Length - indexA;
+ }
+
+ if (length > (strB.Length - indexB)) {
+ len2 = strB.Length - indexB;
+ }
+
+ return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
}
- internal static int _Compare (string strA, int indexA, string strB, int indexB,
- int length, CultureInfo culture,
- _StringCompareMode mode)
+ public int CompareTo (Object value)
+ {
+ if (value == null)
+ return 1;
+
+ if (!(value is String))
+ throw new ArgumentException ();
+
+ return String.Compare (this, (String) value, false);
+ }
+ public int CompareTo (String strB)
{
- int i;
+ if (strB == null)
+ return 1;
- /* When will the hurting stop!?!? */
+ return Compare (this, strB, false);
+ }
+
+ public static int CompareOrdinal (String strA, String strB)
+ {
if (strA == null) {
if (strB == null)
return 0;
else
return -1;
- } else if (strB == null)
+ }
+ else if (strB == null) {
return 1;
+ }
- if (length < 0 || indexA < 0 || indexB < 0)
- throw new ArgumentOutOfRangeException ();
+ /* Invariant, because that is cheaper to
+ * instantiate (and chances are it already has
+ * been.)
+ */
+ return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, CompareOptions.Ordinal);
+ }
- if (indexA > strA.Length || indexB > strB.Length)
+ public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
+ {
+ if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
throw new ArgumentOutOfRangeException ();
- // FIXME: Implement culture
- if (culture != null)
- throw new NotImplementedException ();
-
- for (i = 0; i < length - 1; i++) {
- if ((indexA+i >= strA.Length) || (indexB+i >= strB.Length))
- break;
-
- if (_CompareChar (strA[indexA+i], strB[indexB+i], culture, mode) != 0)
- break;
- }
-
- if (indexA+i >= strA.Length) {
- if (indexB+i >= strB.Length)
+ if (strA == null) {
+ if (strB == null)
return 0;
else
return -1;
- } else if (indexB+i >= strB.Length)
+ }
+ else if (strB == null) {
return 1;
+ }
- return _CompareChar (strA[indexA+i], strB[indexB+i], culture, mode);
- }
-
+ /* Need to cap the requested length to the
+ * length of the string, because
+ * CompareInfo.Compare will insist that length
+ * <= (string.Length - offset)
+ */
+ int len1 = length;
+ int len2 = length;
- public static int Compare (string strA, string strB)
- {
- return Compare (strA, strB, false);
- }
+ if (length > (strA.Length - indexA)) {
+ len1 = strA.Length - indexA;
+ }
- public static int Compare (string strA, string strB, bool ignoreCase)
- {
- return Compare (strA, strB, ignoreCase, null);
+ if (length > (strB.Length - indexB)) {
+ len2 = strB.Length - indexB;
}
- public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
- {
- return Compare (strA, 0, strB, 0,
- _CompareGetLength (strA, strB),
- ignoreCase, culture);
+ return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, CompareOptions.Ordinal);
}
- public static int Compare (string strA, int indexA, string strB, int indexB, int length)
+ public bool EndsWith (String value)
{
- return Compare (strA, indexA, strB, indexB, length, false);
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (value == String.Empty)
+ return true;
+
+ if (value.length > this.length)
+ return false;
+
+ return (0 == Compare (this, length - value.length, value, 0, value.length));
}
- public static int Compare (string strA, int indexA, string strB, int indexB,
- int length, bool ignoreCase)
+ public int IndexOfAny (char [] anyOf)
{
- return Compare (strA, indexA, strB, indexB, length, ignoreCase, null);
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
+
+ return InternalIndexOfAny (anyOf, 0, this.length);
}
- public static int Compare (string strA, int indexA, string strB, int indexB,
- int length, bool ignoreCase, CultureInfo culture)
+ public int IndexOfAny (char [] anyOf, int startIndex)
{
- _StringCompareMode mode;
-
- mode = ignoreCase ? _StringCompareMode.CompareCaseInsensitive :
- _StringCompareMode.CompareDirect;
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
+ if (startIndex < 0 || startIndex >= this.length)
+ throw new ArgumentOutOfRangeException ("sourceIndex");
- return _Compare (strA, indexA, strB, indexB, length, culture, mode);
+ return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
}
- public static int CompareOrdinal (string strA, string strB)
+ public int IndexOfAny (char [] anyOf, int startIndex, int count)
{
- return CompareOrdinal (strA, 0, strB, 0, _CompareGetLength (strA, strB));
- }
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
+ if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+ throw new ArgumentOutOfRangeException ();
- public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
- int length)
- {
- return _Compare (strA, indexA, strB, indexB, length, null,
- _StringCompareMode.CompareOrdinal);
+ return InternalIndexOfAny (anyOf, startIndex, count);
}
- public int CompareTo (object obj)
+ public int IndexOf (char value)
{
- return Compare (this, obj == null ? null : obj.ToString ());
+ return IndexOf (value, 0, this.length);
}
- public int CompareTo (string str)
+ public int IndexOf (String value)
{
- return Compare (this, str);
+ return IndexOf (value, 0, this.length);
}
- public static string Concat (object arg)
+ public int IndexOf (char value, int startIndex)
{
- return arg != null ? arg.ToString () : String.Empty;
+ return IndexOf (value, startIndex, this.length - startIndex);
}
- public static string Concat (params object[] args)
+ public int IndexOf (String value, int startIndex)
{
- string[] strings;
- char[] str;
- int len, i;
-
- if (args == null)
- throw new ArgumentNullException ();
-
- strings = new string [args.Length];
- len = 0;
- i = 0;
- foreach (object arg in args) {
- /* use Empty for each null argument */
- if (arg == null)
- strings[i] = String.Empty;
- else
- strings[i] = arg.ToString ();
- len += strings[i].length;
- i++;
- }
-
- if (len == 0)
- return String.Empty;
-
- String res = new String (len);
- str = res.c_str;
- i = 0;
- for (int j = 0; j < strings.Length; j++)
- for (int k = 0; k < strings[j].length; k++)
- str[i++] = strings[j].c_str[k];
-
- return res;
+ return IndexOf (value, startIndex, this.length - startIndex);
}
- public static string Concat (params string[] values)
+ /* This method is culture-insensitive */
+ public int IndexOf (char value, int startIndex, int count)
{
- int len, i;
- char[] str;
-
- if (values == null)
- throw new ArgumentNullException ();
-
- len = 0;
- foreach (string value in values)
- len += value != null ? value.Length : 0;
-
- if (len == 0)
- return String.Empty;
+ if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+ throw new ArgumentOutOfRangeException ();
- String res = new String (len);
- str = res.c_str;
- i = 0;
- foreach (string value in values) {
- if (value == null)
- continue;
+ if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
+ return -1;
- for (int j = 0; j < value.length; j++)
- str[i++] = value.c_str[j];
+ for (int pos = startIndex; pos < startIndex + count; pos++) {
+ if (this[pos] == value)
+ return(pos);
}
-
- return res;
- }
-
- public static string Concat (object arg0, object arg1)
- {
- string str0 = arg0 != null ? arg0.ToString () : String.Empty;
- string str1 = arg1 != null ? arg1.ToString () : String.Empty;
-
- return Concat (str0, str1);
+ return -1;
}
- public static string Concat (string str0, string str1)
+ /* But this one is culture-sensitive */
+ public int IndexOf (String value, int startIndex, int count)
{
- char[] concat;
- int i, j, len;
+ if (value == null)
+ throw new ArgumentNullException ("value");
- if (str0 == null)
- str0 = String.Empty;
- if (str1 == null)
- str1 = String.Empty;
+ if (startIndex < 0 || count < 0 || startIndex + count > this.length)
+ throw new ArgumentOutOfRangeException ();
- len = str0.length + str1.length;
- if (len == 0)
- return String.Empty;
+ if (value.length == 0)
+ return startIndex;
- String res = new String (len);
+ if (startIndex == 0 && this.length == 0)
+ return -1;
- concat = res.c_str;
- for (i = 0; i < str0.length; i++)
- concat[i] = str0.c_str[i];
- for (j = 0 ; j < str1.length; j++)
- concat[i + j] = str1.c_str[j];
+ if (count == 0)
+ return -1;
- return res;
+ return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
}
- public static string Concat (object arg0, object arg1, object arg2)
+ public int LastIndexOfAny (char [] anyOf)
{
- string str0 = arg0 != null ? arg0.ToString () : String.Empty;
- string str1 = arg1 != null ? arg1.ToString () : String.Empty;
- string str2 = arg2 != null ? arg2.ToString () : String.Empty;
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
- return Concat (str0, str1, str2);
+ return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
}
- public static string Concat (string str0, string str1, string str2)
+ public int LastIndexOfAny (char [] anyOf, int startIndex)
{
- char[] concat;
- int i, j, k, len;
-
- if (str0 == null)
- str0 = String.Empty;
- if (str1 == null)
- str1 = String.Empty;
- if (str2 == null)
- str2 = String.Empty;
-
- len = str0.length + str1.length + str2.length;
- if (len == 0)
- return String.Empty;
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
- String res = new String (len);
+ if (startIndex < 0 || startIndex > this.length)
+ throw new ArgumentOutOfRangeException ();
- concat = res.c_str;
- for (i = 0; i < str0.length; i++)
- concat[i] = str0.c_str[i];
- for (j = 0; j < str1.length; j++)
- concat[i + j] = str1.c_str[j];
- for (k = 0; k < str2.length; k++)
- concat[i + j + k] = str2.c_str[k];
+ if (this.length == 0)
+ return -1;
- return res;
+ return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
}
- public static string Concat (string str0, string str1, string str2, string str3)
+ public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
{
- char[] concat;
- int i, j, k, l, len;
+ if (anyOf == null)
+ throw new ArgumentNullException ("anyOf");
- if (str0 == null)
- str0 = String.Empty;
- if (str1 == null)
- str1 = String.Empty;
- if (str2 == null)
- str2 = String.Empty;
- if (str3 == null)
- str3 = String.Empty;
+ if (startIndex < 0 || count < 0 || startIndex > this.length || startIndex - count < -1)
+ throw new ArgumentOutOfRangeException ();
- len = str0.length + str1.length + str2.length + str3.length;
- if (len == 0)
- return String.Empty;
- String res = new String (len);
+ if (this.length == 0)
+ return -1;
- concat = res.c_str;
- for (i = 0; i < str0.length; i++)
- concat[i] = str0.c_str[i];
- for (j = 0; j < str1.length; j++)
- concat[i + j] = str1.c_str[j];
- for (k = 0; k < str2.length; k++)
- concat[i + j + k] = str2.c_str[k];
- for (l = 0; l < str3.length; l++)
- concat[i + j + k + l] = str3.c_str[l];
+ return InternalLastIndexOfAny (anyOf, startIndex, count);
+ }
- return res;
+ public int LastIndexOf (char value)
+ {
+ if (this.length == 0)
+ return -1;
+ else
+ return LastIndexOf (value, this.length - 1, this.length);
}
- public static string Copy (string str)
+ public int LastIndexOf (String value)
{
- // FIXME: how do I *copy* a string if I can only have 1 of each?
- if (str == null)
- throw new ArgumentNullException ();
+ if (this.length == 0)
+ /* This overload does additional checking */
+ return LastIndexOf (value, 0, 0);
+ else
+ return LastIndexOf (value, this.length - 1, this.length);
+ }
- return str;
+ public int LastIndexOf (char value, int startIndex)
+ {
+ return LastIndexOf (value, startIndex, startIndex + 1);
}
- public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
+ public int LastIndexOf (String value, int startIndex)
{
- // LAMESPEC: should I null-terminate?
- int i;
+ return LastIndexOf (value, startIndex, startIndex + 1);
+ }
- if (destination == null)
- throw new ArgumentNullException ();
+ /* This method is culture-insensitive */
+ public int LastIndexOf (char value, int startIndex, int count)
+ {
+ if (startIndex == 0 && this.length == 0)
+ return -1;
- if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
+ if (startIndex < 0 || count < 0)
throw new ArgumentOutOfRangeException ();
- if (sourceIndex + count > this.length)
+ if (startIndex >= this.length || startIndex - count + 1 < 0)
throw new ArgumentOutOfRangeException ();
- if (destinationIndex + count > destination.Length)
- throw new ArgumentOutOfRangeException ();
-
- for (i = 0; i < count; i++)
- destination[destinationIndex + i] = this.c_str[sourceIndex + i];
+ for(int pos = startIndex; pos > startIndex - count; pos--) {
+ if (this [pos] == value)
+ return pos;
+ }
+ return -1;
}
- public bool EndsWith (string value)
+ /* But this one is culture-sensitive */
+ public int LastIndexOf (String value, int startIndex, int count)
{
- bool endswith = true;
- int start, i;
-
if (value == null)
- throw new ArgumentNullException ();
-
- start = this.length - value.length;
- if (start < 0)
- return false;
+ throw new ArgumentNullException ("value");
- for (i = start; i < this.length && endswith; i++)
- endswith = this.c_str[i] == value.c_str[i - start];
-
- return endswith;
- }
+ if (value == String.Empty)
+ return 0;
- public override bool Equals (object obj)
- {
- if (!(obj is String))
- return false;
+ if (startIndex == 0 && this.length == 0)
+ return -1;
- return this == (String) obj;
- }
+ // This check is needed to match undocumented MS behaviour
+ if (this.length == 0 && value.length > 0)
+ return -1;
- public bool Equals (string value)
- {
- return this == value;
- }
+ if (value.length > startIndex)
+ return -1;
- public static bool Equals (string a, string b)
- {
- return a == b;
- }
+ if (count == 0)
+ return -1;
- public static string Format (string format, object arg0) {
- return Format (null, format, new object[] { arg0 });
- }
+ if (startIndex < 0 || startIndex > this.length)
+ throw new ArgumentOutOfRangeException ();
- public static string Format (string format, object arg0, object arg1) {
- return Format (null, format, new object[] { arg0, arg1 });
- }
+ if (count < 0 || startIndex - count + 1 < 0)
+ throw new ArgumentOutOfRangeException ();
- public static string Format (string format, object arg0, object arg1, object arg2) {
- return Format (null, format, new object[] { arg0, arg1, arg2 });
+ return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
}
- public static string Format (string format, params object[] args) {
- return Format (null, format, args);
+ public String PadLeft (int totalWidth)
+ {
+ return PadLeft (totalWidth, ' ');
}
- public static string Format (IFormatProvider provider, string format, params object[] args) {
- if (format == null || args == null)
- throw new ArgumentNullException ();
-
- StringBuilder result = new StringBuilder ();
-
- int ptr = 0;
- int start = ptr;
- while (ptr < format.Length) {
- char c = format[ptr ++];
-
- if (c == '{') {
- result.Append (format, start, ptr - start - 1);
-
- // check for escaped open bracket
-
- if (format[ptr] == '{') {
- start = ptr ++;
- break;
- }
-
- // parse specifier
-
- int n, width;
- bool left_align;
- string arg_format;
-
- ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
- if (n >= args.Length)
- throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
-
- // format argument
-
- object arg = args[n];
-
- string str;
- if (arg == null)
- str = "";
- else if (arg is IFormattable)
- str = ((IFormattable)arg).ToString (arg_format, provider);
- else
- str = arg.ToString ();
-
- // pad formatted string and append to result
-
- if (width > str.Length) {
- string pad = new String (' ', width - str.Length);
-
- if (left_align) {
- result.Append (str);
- result.Append (pad);
- }
- else {
- result.Append (pad);
- result.Append (str);
- }
- }
- else
- result.Append (str);
-
- start = ptr;
- }
- else if (c == '}' && format[ptr] == '}') {
- result.Append (format, start, ptr - start - 1);
- start = ptr ++;
- }
- }
+ public String PadLeft (int totalWidth, char paddingChar)
+ {
+ if (totalWidth < 0)
+ throw new ArgumentException ();
- if (start < format.Length)
- result.Append (format.Substring (start));
+ if (totalWidth < this.length)
+ return String.Copy (this);
- return result.ToString ();
+ return InternalPad (totalWidth, paddingChar, false);
}
- public CharEnumerator GetEnumerator ()
- {
- return new CharEnumerator (this);
- }
-
- IEnumerator IEnumerable.GetEnumerator ()
+ public String PadRight (int totalWidth)
{
- return new CharEnumerator (this);
+ return PadRight (totalWidth, ' ');
}
- public override int GetHashCode ()
+ public String PadRight (int totalWidth, char paddingChar)
{
- int h = 0;
- int i;
- for (i = 0; i < length; ++i)
- h = (h << 5) - h + c_str [i];
- return h;
- }
+ if (totalWidth < 0)
+ throw new ArgumentException ();
- public TypeCode GetTypeCode ()
- {
- return TypeCode.String;
- }
+ if (totalWidth < this.length)
+ return String.Copy (this);
- public int IndexOf (char value)
- {
- return IndexOf (value, 0, this.length);
+ return InternalPad (totalWidth, paddingChar, true);
}
- public int IndexOf (string value)
+ public bool StartsWith (String value)
{
- return IndexOf (value, 0, this.length);
- }
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (value == String.Empty)
+ return true;
- public int IndexOf (char value, int startIndex)
- {
- return IndexOf (value, startIndex, this.length - startIndex);
+ if (this.length < value.length)
+ return false;
+
+ return (0 == Compare (this, 0, value, 0 , value.length));
}
- public int IndexOf (string value, int startIndex)
+ /* This method is culture insensitive */
+ public String Replace (char oldChar, char newChar)
{
- return IndexOf (value, startIndex, this.length - startIndex);
+ return InternalReplace (oldChar, newChar);
}
- public int IndexOf (char value, int startIndex, int count)
+ /* This method is culture sensitive */
+ public String Replace (String oldValue, String newValue)
{
- int i;
+ if (oldValue == null)
+ throw new ArgumentNullException ("oldValue");
- if (startIndex < 0 || count < 0 || startIndex + count > this.length)
- throw new ArgumentOutOfRangeException ();
+ if (oldValue == String.Empty)
+ throw new ArgumentException ("oldValue is the empty string.");
- for (i = startIndex; i - startIndex < count; i++)
- if (this.c_str[i] == value)
- return i;
+ if (this == String.Empty)
+ return this;
- return -1;
+ if (oldValue.Length == 0 || oldValue[0] == '\0') {
+ return(this);
+ }
+
+ if (newValue == null)
+ newValue = String.Empty;
+
+ return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
}
- public int IndexOf (string value, int startIndex, int count)
+ public String Remove (int startIndex, int count)
{
- if (value == null)
- throw new ArgumentNullException ();
-
if (startIndex < 0 || count < 0 || startIndex + count > this.length)
throw new ArgumentOutOfRangeException ();
- return BoyerMoore (this.c_str, value, startIndex, count);
-#if XXX
- int i;
- for (i = startIndex; i - startIndex + value.Length <= count; ) {
- if (this.c_str[i] == value[0]) {
- bool equal = true;
- int j, nexti = 0;
-
- for (j = 1; equal && j < value.Length; j++) {
- equal = this.c_str[i + j] == value[j];
- if (this.c_str[i + j] == value[0] && nexti == 0)
- nexti = i + j;
- }
-
- if (equal)
- return i;
-
- if (nexti != 0)
- i = nexti;
- else
- i += j;
- } else
- i++;
- }
-
- return -1;
-#endif
+ return InternalRemove (startIndex, count);
}
- public int IndexOfAny (char[] values)
+ public String ToLower ()
{
- return IndexOfAny (values, 0, this.length);
+ return InternalToLower (CultureInfo.CurrentCulture);
}
- public int IndexOfAny (char[] values, int startIndex)
+ public String ToLower (CultureInfo culture)
{
- return IndexOfAny (values, startIndex, this.length - startIndex);
+ return InternalToLower (culture);
}
- public int IndexOfAny (char[] values, int startIndex, int count)
+ public String ToUpper ()
{
- if (values == null)
- throw new ArgumentNullException ();
-
- if (startIndex < 0 || count < 0 || startIndex + count > this.length)
- throw new ArgumentOutOfRangeException ();
-
- for (int i = startIndex; i < startIndex + count; i++) {
- for (int j = 0; j < strlen (values); j++) {
- if (this.c_str[i] == values[j])
- return i;
- }
- }
-
- return -1;
+ return InternalToUpper (CultureInfo.CurrentCulture);
}
- public string Insert (int startIndex, string value)
+ public String ToUpper (CultureInfo culture)
{
- char[] str;
- int i, j;
-
- if (value == null)
- throw new ArgumentNullException ();
-
- if (startIndex < 0 || startIndex > this.length)
- throw new ArgumentOutOfRangeException ();
-
- String res = new String (value.length + this.length);
-
- str = res.c_str;
- for (i = 0; i < startIndex; i++)
- str[i] = this.c_str[i];
- for (j = 0; j < value.length; j++)
- str[i + j] = value.c_str[j];
- for ( ; i < this.length; i++)
- str[i + j] = this.c_str[i];
-
- return res;
+ return InternalToUpper (culture);
}
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern static string Intern (string str);
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern static string IsInterned (string str);
-
- public static string Join (string separator, string[] value)
+ public override String ToString ()
{
- return Join (separator, value, 0, value.Length);
+ return this;
}
- public static string Join (string separator, string[] value, int startIndex, int count)
+ public String ToString (IFormatProvider provider)
{
- // LAMESPEC: msdn doesn't specify what happens when separator is null
- int len, i, j, used;
- char[] str;
-
- if (separator == null || value == null)
- throw new ArgumentNullException ();
-
- if (startIndex + count > value.Length)
- throw new ArgumentOutOfRangeException ();
-
- len = 0;
- for (i = startIndex, used = 0; used < count; i++, used++) {
- if (i != startIndex)
- len += separator.length;
-
- len += value[i].length;
- }
-
- // We have no elements to join?
- if (i == startIndex)
- return String.Empty;
-
- String res = new String (len);
-
- str = res.c_str;
- for (i = 0; i < value[startIndex].length; i++)
- str[i] = value[startIndex][i];
-
- used = 1;
- for (j = startIndex + 1; used < count; j++, used++) {
- int k;
-
- for (k = 0; k < separator.length; k++)
- str[i++] = separator.c_str[k];
- for (k = 0; k < value[j].length; k++)
- str[i++] = value[j].c_str[k];
- }
-
- return res;
+ return this;
}
- public int LastIndexOf (char value)
+ public String Trim ()
{
- int i = this.length;
- if (i == 0)
- return -1;
- --i;
- for (; i >= 0; i--) {
- if (this.c_str[i] == value)
- return i;
- }
-
- return -1;
+ return Trim (null);
}
- public int LastIndexOf (string value)
+ public static String Format (String format, Object arg0)
{
- return LastIndexOf (value, this.length - 1, this.length);
+ return Format (null, format, new Object[] {arg0});
}
- public int LastIndexOf (char value, int startIndex)
+ public static String Format (String format, Object arg0, Object arg1)
{
- if (startIndex < 0 || startIndex >= this.length)
- throw new ArgumentOutOfRangeException ();
-
- for (int i = startIndex; i >= 0; i--) {
- if (this.c_str[i] == value)
- return i;
- }
-
- return -1;
+ return Format (null, format, new Object[] {arg0, arg1});
}
- public int LastIndexOf (string value, int startIndex)
+ public static String Format (String format, Object arg0, Object arg1, Object arg2)
{
- return LastIndexOf (value, startIndex, startIndex + 1);
+ return Format (null, format, new Object[] {arg0, arg1, arg2});
}
- public int LastIndexOf (char value, int startIndex, int count)
+ public static string Format (string format, params object[] args)
{
- if (startIndex < 0 || count < 0)
- throw new ArgumentOutOfRangeException ();
-
- if (startIndex >= this.length || startIndex - count + 1 < 0)
- throw new ArgumentOutOfRangeException ();
-
- for (int i = startIndex; i > startIndex - count; i--) {
- if (this.c_str[i] == value)
- return i;
- }
-
- return -1;
+ return Format (null, format, args);
}
-
- public int LastIndexOf (string value, int startIndex, int count)
+
+ public static string Format (IFormatProvider provider, string format, params object[] args)
{
- // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
- // but maybe it's the end-position in MS's implementation?
- // msdn is unclear on this point. I think this is correct though.
- int i, len;
-
- if (value == null)
+ StringBuilder b = new StringBuilder ();
+ FormatHelper (b, provider, format, args);
+ return b.ToString ();
+ }
+
+ internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
+ {
+ if (format == null || args == null)
throw new ArgumentNullException ();
- if (startIndex < 0 || startIndex >= this.length)
- throw new ArgumentOutOfRangeException ();
-
- if (count < 0 || startIndex - count + 1 < 0)
- throw new ArgumentOutOfRangeException ();
-
- if (value == String.Empty)
- return startIndex;
+ int ptr = 0;
+ int start = ptr;
+ while (ptr < format.length) {
+ char c = format[ptr ++];
- if (startIndex + value.length > this.length) {
- /* just a little optimization */
- int start;
+ if (c == '{') {
+ result.Append (format, start, ptr - start - 1);
- start = this.length - value.length;
- count -= startIndex - start;
- startIndex = start;
- }
+ // check for escaped open bracket
- // FIXME: use a reversed-unicode-safe-Boyer-Moore?
- len = value.length - 1;
- for (i = startIndex; i > startIndex - count; i--) {
- if (this.c_str[i + len] == value.c_str[len]) {
- bool equal = true;
- int j;
+ if (format[ptr] == '{') {
+ start = ptr ++;
+ continue;
+ }
- for (j = len - 1; equal && j >= 0; j--)
- equal = this.c_str[i + j] == value.c_str[j];
+ // parse specifier
+
+ int n, width;
+ bool left_align;
+ string arg_format;
- if (equal)
- return i;
- }
- }
+ ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
+ if (n >= args.Length)
+ throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
- return -1;
- }
+ // format argument
- public int LastIndexOfAny (char[] values)
- {
- return LastIndexOfAny (values, this.length - 1, this.length);
- }
+ object arg = args[n];
- public int LastIndexOfAny (char[] values, int startIndex)
- {
- return LastIndexOfAny (values, startIndex, startIndex + 1);
- }
+ string str;
+ if (arg == null)
+ str = "";
+ else if (arg is IFormattable)
+ str = ((IFormattable)arg).ToString (arg_format, provider);
+ else
+ str = arg.ToString ();
- public int LastIndexOfAny (char[] values, int startIndex, int count)
- {
- int i;
+ // pad formatted string and append to result
- if (values == null)
- throw new ArgumentNullException ();
+ if (width > str.length) {
+ string pad = new String (' ', width - str.length);
- if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
- throw new ArgumentOutOfRangeException ();
+ if (left_align) {
+ result.Append (str);
+ result.Append (pad);
+ }
+ else {
+ result.Append (pad);
+ result.Append (str);
+ }
+ }
+ else
+ result.Append (str);
- for (i = startIndex; i > startIndex - count; i--) {
- for (int j = 0; j < strlen (values); j++) {
- if (this.c_str[i] == values[j])
- return i;
+ start = ptr;
+ }
+ else if (c == '}' && ptr < format.length && format[ptr] == '}') {
+ result.Append (format, start, ptr - start - 1);
+ start = ptr ++;
+ }
+ else if (c == '}') {
+ throw new FormatException ("Input string was not in a correct format.");
}
}
- return -1;
- }
-
- public string PadLeft (int totalWidth)
- {
- return PadLeft (totalWidth, ' ');
+ if (start < format.length)
+ result.Append (format.Substring (start));
}
- public string PadLeft (int totalWidth, char padChar)
+ public static String Copy (String str)
{
- char[] str;
- int i, j;
-
- if (totalWidth < 0)
- throw new ArgumentException ();
-
- str = new char [totalWidth > this.length ? totalWidth : this.length];
- for (i = 0; i < totalWidth - this.length; i++)
- str[i] = padChar;
+ if (str == null)
+ throw new ArgumentNullException ("str");
- for (j = 0; j < this.length; i++, j++)
- str[i] = this.c_str[j];
+ int length = str.length;
- return new String (str);
+ String tmp = InternalAllocateStr (length);
+ InternalStrcpy (tmp, 0, str);
+ return tmp;
}
- public string PadRight (int totalWidth)
+ public static String Concat (Object obj)
{
- return PadRight (totalWidth, ' ');
+ if (obj == null)
+ return String.Empty;
+
+ return obj.ToString ();
}
- public string PadRight (int totalWidth, char padChar)
+ public static String Concat (Object obj1, Object obj2)
{
- char[] str;
- int i;
+ string s1, s2;
- if (totalWidth < 0)
- throw new ArgumentException ();
-
- str = new char [totalWidth > this.length ? totalWidth : this.length];
- for (i = 0; i < this.length; i++)
- str[i] = this.c_str[i];
+ s1 = (obj1 != null) ? obj1.ToString () : null;
+ s2 = (obj2 != null) ? obj2.ToString () : null;
+
+ if (s1 == null) {
+ if (s2 == null)
+ return String.Empty;
+ else
+ return s2;
+ } else if (s2 == null)
+ return s1;
- for ( ; i < str.Length; i++)
- str[i] = padChar;
+ String tmp = InternalAllocateStr (s1.Length + s2.Length);
+ InternalStrcpy (tmp, 0, s1);
+ InternalStrcpy (tmp, s1.length, s2);
- return new String (str);
+ return tmp;
}
- public string Remove (int startIndex, int count)
+ public static String Concat (Object obj1, Object obj2, Object obj3)
{
- char[] str;
- int i, j, len;
+ string s1, s2, s3;
+ if (obj1 == null)
+ s1 = String.Empty;
+ else
+ s1 = obj1.ToString ();
- if (startIndex < 0 || count < 0 || startIndex + count > this.length)
- throw new ArgumentOutOfRangeException ();
+ if (obj2 == null)
+ s2 = String.Empty;
+ else
+ s2 = obj2.ToString ();
- len = this.length - count;
- if (len == 0)
- return String.Empty;
-
- String res = new String (len);
- str = res.c_str;
- for (i = 0; i < startIndex; i++)
- str[i] = this.c_str[i];
- for (j = i + count; j < this.length; j++)
- str[i++] = this.c_str[j];
+ if (obj3 == null)
+ s3 = String.Empty;
+ else
+ s3 = obj3.ToString ();
- return res;
+ return Concat (s1, s2, s3);
}
- public string Replace (char oldChar, char newChar)
+ public static String Concat (Object obj1, Object obj2, Object obj3, Object obj4)
{
- char[] str;
- int i;
+ string s1, s2, s3, s4;
- String res = new String (length);
- str = res.c_str;
- for (i = 0; i < this.length; i++) {
- if (this.c_str[i] == oldChar)
- str[i] = newChar;
- else
- str[i] = this.c_str[i];
- }
+ if (obj1 == null)
+ s1 = String.Empty;
+ else
+ s1 = obj1.ToString ();
- return res;
- }
+ if (obj2 == null)
+ s2 = String.Empty;
+ else
+ s2 = obj2.ToString ();
- public string Replace (string oldValue, string newValue)
- {
- // LAMESPEC: msdn doesn't specify what to do if either args is null
- int index, len, i, j;
- char[] str;
+ if (obj3 == null)
+ s3 = String.Empty;
+ else
+ s3 = obj3.ToString ();
- if (oldValue == null || newValue == null)
- throw new ArgumentNullException ();
+ if (obj4 == null)
+ s4 = String.Empty;
+ else
+ s4 = obj4.ToString ();
+
+ return Concat (s1, s2, s3, s4);
+
+ }
- // Use IndexOf in case I later rewrite it to use Boyer-Moore
- index = IndexOf (oldValue, 0);
- if (index == -1) {
- // This is the easy one ;-)
- return Substring (0, this.length);
+ public static String Concat (String s1, String s2)
+ {
+ if (s1 == null) {
+ if (s2 == null)
+ return String.Empty;
+ return s2;
}
- len = this.length - oldValue.length + newValue.length;
- if (len == 0)
- return String.Empty;
+ if (s2 == null)
+ return s1;
- String res = new String (len);
- str = res.c_str;
- for (i = 0; i < index; i++)
- str[i] = this.c_str[i];
- for (j = 0; j < newValue.length; j++)
- str[i++] = newValue[j];
- for (j = index + oldValue.length; j < this.length; j++)
- str[i++] = this.c_str[j];
+ String tmp = InternalAllocateStr (s1.length + s2.length);
- return res;
+ InternalStrcpy (tmp, 0, s1);
+ InternalStrcpy (tmp, s1.length, s2);
+
+ return tmp;
}
- private int splitme (char[] separators, int startIndex)
+ public static String Concat (String s1, String s2, String s3)
{
- /* this is basically a customized IndexOfAny() for the Split() methods */
- for (int i = startIndex; i < this.length; i++) {
- if (separators != null) {
- foreach (char sep in separators) {
- if (this.c_str[i] == sep)
- return i - startIndex;
- }
- } else if (is_lwsp (this.c_str[i])) {
- return i - startIndex;
+ if (s1 == null){
+ if (s2 == null){
+ if (s3 == null)
+ return String.Empty;
+ return s3;
+ } else {
+ if (s3 == null)
+ return s2;
+ }
+ s1 = String.Empty;
+ } else {
+ if (s2 == null){
+ if (s3 == null)
+ return s1;
+ else
+ s2 = String.Empty;
+ } else {
+ if (s3 == null)
+ s3 = String.Empty;
}
}
- return -1;
+ //return InternalConcat (s1, s2, s3);
+ String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
+
+ InternalStrcpy (tmp, 0, s1);
+ InternalStrcpy (tmp, s1.length, s2);
+ InternalStrcpy (tmp, s1.length + s2.length, s3);
+
+ return tmp;
}
- public string[] Split (params char[] separator)
- {
- /**
- * split:
- * @separator: delimiting chars or null to split on whtspc
- *
- * Returns: 1. An array consisting of a single
- * element (@this) if none of the delimiting
- * chars appear in @this. 2. An array of
- * substrings which are delimited by one of
- * the separator chars. 3. An array of
- * substrings separated by whitespace if
- * @separator is null. The Empty string should
- * be returned wherever 2 delimiting chars are
- * adjacent.
- **/
- // FIXME: would using a Queue be better?
- string[] strings;
- ArrayList list;
- int index, len;
-
- list = new ArrayList ();
- for (index = 0, len = 0; index < this.length; index += len + 1) {
- len = splitme (separator, index);
- len = len > -1 ? len : this.length - index;
- if (len == 0) {
- list.Add (String.Empty);
- } else {
- char[] str;
- int i;
+ public static String Concat (String s1, String s2, String s3, String s4)
+ {
+ if (s1 == null && s2 == null && s3 == null && s4 == null)
+ return String.Empty;
- str = new char [len];
- for (i = 0; i < len; i++)
- str[i] = this.c_str[index + i];
+ if (s1 == null)
+ s1 = String.Empty;
+ if (s2 == null)
+ s2 = String.Empty;
+ if (s3 == null)
+ s3 = String.Empty;
+ if (s4 == null)
+ s4 = String.Empty;
- list.Add (new String (str));
- }
- }
+ String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
- strings = new string [list.Count];
- if (list.Count == 1) {
- /* special case for an array holding @this */
- strings[0] = this;
- } else {
- for (index = 0; index < list.Count; index++)
- strings[index] = (string) list[index];
- }
+ InternalStrcpy (tmp, 0, s1);
+ InternalStrcpy (tmp, s1.length, s2);
+ InternalStrcpy (tmp, s1.length + s2.length, s3);
+ InternalStrcpy (tmp, s1.length + s2.length + s3.length, s4);
- return strings;
+ return tmp;
}
- public string[] Split (char[] separator, int maxCount)
+ public static String Concat (params Object[] args)
{
- // FIXME: what to do if maxCount <= 0?
- // FIXME: would using Queue be better than ArrayList?
- string[] strings;
- ArrayList list;
- int index, len, used;
-
- used = 0;
- list = new ArrayList ();
- for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
- len = splitme (separator, index);
- len = len > -1 ? len : this.length - index;
- if (len == 0) {
- list.Add (String.Empty);
- } else {
- char[] str;
- int i;
+ string [] strings;
+ int len, i, currentpos;
- str = new char [len];
- for (i = 0; i < len; i++)
- str[i] = this.c_str[index + i];
+ if (args == null)
+ throw new ArgumentNullException ("args");
- list.Add (new String (str));
- }
- used++;
+ strings = new string [args.Length];
+ len = 0;
+ i = 0;
+ foreach (object arg in args) {
+ /* use Empty for each null argument */
+ if (arg == null)
+ strings[i] = String.Empty;
+ else
+ strings[i] = arg.ToString ();
+ len += strings[i].length;
+ i++;
}
- /* fit the remaining chunk of the @this into it's own element */
- if (index < this.length - 1) {
- char[] str;
- int i;
-
- str = new char [this.length - index];
- for (i = index; i < this.length; i++)
- str[i - index] = this.c_str[i];
+ if (len == 0)
+ return String.Empty;
- list.Add (new String (str));
- }
+ currentpos = 0;
- strings = new string [list.Count];
- if (list.Count == 1) {
- /* special case for an array holding @this */
- strings[0] = this;
- } else {
- for (index = 0; index < list.Count; index++)
- strings[index] = (string) list[index];
+ String tmp = InternalAllocateStr (len);
+ for (i = 0; i < strings.Length; i++) {
+ InternalStrcpy (tmp, currentpos, strings[i]);
+ currentpos += strings[i].length;
}
- return strings;
+ return tmp;
}
- public bool StartsWith (string value)
+ public static String Concat (params String[] values)
{
- bool startswith = true;
- int i;
+ int len, i, currentpos;
- if (value == null)
- throw new ArgumentNullException ();
+ if (values == null)
+ throw new ArgumentNullException ("values");
- if (value.length > this.length)
- return false;
+ len = 0;
+ foreach (string value in values)
+ len += value != null ? value.length : 0;
- for (i = 0; i < value.length && startswith; i++)
- startswith = startswith && value.c_str[i] == this.c_str[i];
+ if (len == 0)
+ return String.Empty;
+
+ currentpos = 0;
+
+ String tmp = InternalAllocateStr (len);
+ for (i = 0; i < values.Length; i++) {
+ if (values[i] == null)
+ continue;
+
+ InternalStrcpy (tmp, currentpos, values[i]);
+ currentpos += values[i].length;
+ }
- return startswith;
+ return tmp;
}
- public string Substring (int startIndex)
+ public String Insert (int startIndex, String value)
{
- char[] str;
- int i, len;
+ if (value == null)
+ throw new ArgumentNullException ("value");
if (startIndex < 0 || startIndex > this.length)
throw new ArgumentOutOfRangeException ();
- len = this.length - startIndex;
- if (len == 0)
- return String.Empty;
- String res = new String (len);
- str = res.c_str;
- for (i = startIndex; i < this.length; i++)
- str[i - startIndex] = this.c_str[i];
+ return InternalInsert (startIndex, value);
+ }
+
+
+ public static string Intern (string str)
+ {
+ if (str == null)
+ throw new ArgumentNullException ("str");
- return res;
+ return InternalIntern (str);
}
- public string Substring (int startIndex, int length)
+ public static string IsInterned (string str)
{
- char[] str;
- int i;
+ if (str == null)
+ throw new ArgumentNullException ("str");
- if (startIndex < 0 || length < 0 || startIndex + length > this.length)
+ return InternalIsInterned (str);
+ }
+
+ public static string Join (string separator, string [] value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return Join (separator, value, 0, value.Length);
+ }
+
+ public static string Join (string separator, string[] value, int startIndex, int count)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (startIndex + count > value.Length)
throw new ArgumentOutOfRangeException ();
- if (length == 0)
+ if (startIndex == value.Length)
return String.Empty;
-
- String res = new String (length);
- str = res.c_str;
- for (i = startIndex; i < startIndex + length; i++)
- str[i - startIndex] = this.c_str[i];
- return res;
+ return InternalJoin (separator, value, startIndex, count);
}
bool IConvertible.ToBoolean (IFormatProvider provider)
{
- return Convert.ToBoolean (this);
- }
-
- byte IConvertible.ToByte (IFormatProvider provider)
- {
- return Convert.ToByte (this);
- }
-
- char IConvertible.ToChar (IFormatProvider provider)
- {
- return Convert.ToChar (this);
+ return Convert.ToBoolean (this, provider);
}
- public char[] ToCharArray ()
+ byte IConvertible.ToByte (IFormatProvider provider)
{
- return ToCharArray (0, this.length);
+ return Convert.ToByte (this, provider);
}
- public char[] ToCharArray (int startIndex, int length)
+ char IConvertible.ToChar (IFormatProvider provider)
{
- char[] chars;
- int i;
-
- if (startIndex < 0 || length < 0 || startIndex + length > this.length)
- throw new ArgumentOutOfRangeException ();
-
- chars = new char [length];
- for (i = startIndex; i < length; i++)
- chars[i - startIndex] = this.c_str[i];
-
- return chars;
+ return Convert.ToChar (this, provider);
}
DateTime IConvertible.ToDateTime (IFormatProvider provider)
{
- return Convert.ToDateTime (this);
+ return Convert.ToDateTime (this, provider);
}
decimal IConvertible.ToDecimal (IFormatProvider provider)
{
- return Convert.ToDecimal (this);
+ return Convert.ToDecimal (this, provider);
}
double IConvertible.ToDouble (IFormatProvider provider)
{
- return Convert.ToDouble (this);
+ return Convert.ToDouble (this, provider);
}
short IConvertible.ToInt16 (IFormatProvider provider)
{
- return Convert.ToInt16 (this);
+ return Convert.ToInt16 (this, provider);
}
int IConvertible.ToInt32 (IFormatProvider provider)
{
- return Convert.ToInt32 (this);
+ return Convert.ToInt32 (this, provider);
}
long IConvertible.ToInt64 (IFormatProvider provider)
{
- return Convert.ToInt64 (this);
+ return Convert.ToInt64 (this, provider);
}
-
- public string ToLower ()
- {
- char[] str;
- int i;
-
- String res = new String (length);
- str = res.c_str;
- for (i = 0; i < this.length; i++)
- str[i] = Char.ToLower (this.c_str[i]);
-
- return res;
- }
-
- [MonoTODO]
- public string ToLower (CultureInfo culture)
- {
- // FIXME: implement me
- throw new NotImplementedException ();
-
- }
-
- [CLSCompliant(false)]
+
+ [CLSCompliant (false)]
sbyte IConvertible.ToSByte (IFormatProvider provider)
{
- return Convert.ToSByte (this);
+ return Convert.ToSByte (this, provider);
}
float IConvertible.ToSingle (IFormatProvider provider)
{
- return Convert.ToSingle (this);
- }
-
- public override string ToString ()
- {
- return this;
+ return Convert.ToSingle (this, provider);
}
string IConvertible.ToString (IFormatProvider format)
return Convert.ToType (this, conversionType, provider);
}
- [CLSCompliant(false)]
+ [CLSCompliant (false)]
ushort IConvertible.ToUInt16 (IFormatProvider provider)
{
- return Convert.ToUInt16 (this);
+ return Convert.ToUInt16 (this, provider);
}
- [CLSCompliant(false)]
+ [CLSCompliant (false)]
uint IConvertible.ToUInt32 (IFormatProvider provider)
{
- return Convert.ToUInt32 (this);
+ return Convert.ToUInt32 (this, provider);
}
- [CLSCompliant(false)]
+ [CLSCompliant (false)]
ulong IConvertible.ToUInt64 (IFormatProvider provider)
{
- return Convert.ToUInt64 (this);
- }
-
- public string ToUpper ()
- {
- char[] str;
- int i;
-
- String res = new String (length);
- str = res.c_str;
- for (i = 0; i < this.length; i++)
- str[i] = Char.ToUpper (this.c_str[i]);
-
- return res;
- }
-
- [MonoTODO]
- public string ToUpper (CultureInfo culture)
- {
- // FIXME: implement me
- throw new NotImplementedException ();
- }
-
- public string Trim ()
- {
- return Trim (null);
+ return Convert.ToUInt64 (this, provider);
}
- public string Trim (params char[] trimChars)
+ TypeCode IConvertible.GetTypeCode ()
{
- int begin, end;
- bool matches = false;
-
- for (begin = 0; begin < this.length; begin++) {
- if (trimChars != null) {
- matches = false;
- foreach (char c in trimChars) {
- matches = this.c_str[begin] == c;
- if (matches)
- break;
- }
- if (matches)
- continue;
- } else {
- matches = is_lwsp (this.c_str[begin]);
- if (matches)
- continue;
- }
- break;
- }
-
- for (end = this.length - 1; end > begin; end--) {
- if (trimChars != null) {
- matches = false;
- foreach (char c in trimChars) {
- matches = this.c_str[end] == c;
- if (matches)
- break;
- }
- if (matches)
- continue;
- } else {
- matches = is_lwsp (this.c_str[end]);
- if (matches)
- continue;
- }
- break;
- }
- end++;
-
- if (begin >= end)
- return String.Empty;
-
- return Substring (begin, end - begin);
+ return TypeCode.String;
}
- public string TrimEnd (params char[] trimChars)
- {
- bool matches = true;
- int end;
-
- for (end = this.length - 1; matches && end > 0; end--) {
-
- if (trimChars != null) {
- matches = false;
- foreach (char c in trimChars) {
- matches = this.c_str[end] == c;
- if (matches)
- break;
- }
- } else {
- matches = is_lwsp (this.c_str[end]);
- }
-
- if (!matches)
- return Substring (0, end+1);
+ public int Length {
+ get {
+ return length;
}
-
- if (end == 0)
- return String.Empty;
-
- return Substring (0, end);
}
- public string TrimStart (params char[] trimChars)
+ public CharEnumerator GetEnumerator ()
{
- bool matches = true;
- int begin;
-
- for (begin = 0; matches && begin < this.length; begin++) {
- if (trimChars != null) {
- matches = false;
- foreach (char c in trimChars) {
- matches = this.c_str[begin] == c;
- if (matches)
- break;
- }
- } else {
- matches = is_lwsp (this.c_str[begin]);
- }
-
- if (!matches)
- return Substring (begin, this.length - begin);
- }
-
- return String.Empty;
+ return new CharEnumerator (this);
}
- // Operators
- public static bool operator ==(string a, string b)
+ IEnumerator IEnumerable.GetEnumerator ()
{
- if ((object)a == null) {
- if ((object)b == null)
- return true;
- return false;
- }
- if ((object)b == null)
- return false;
-
- if (a.length != b.length)
- return false;
-
- int l = a.length;
- for (int i = 0; i < l; i++)
- if (a.c_str[i] != b.c_str[i])
- return false;
-
- return true;
+ return new CharEnumerator (this);
}
- public static bool operator !=(string a, string b)
+ private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
+ out bool left_align, out string format)
{
- return !(a == b);
- }
-
- // private
-
- private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
// parses format specifier of form:
- // N,[[-]M][:F]}
+ // N,[\ +[-]M][:F]}
//
// where:
try {
// N = argument number (non-negative integer)
-
+
n = ParseDecimal (str, ref ptr);
if (n < 0)
- throw new FormatException ("Input string was not in correct format.");
-
+ throw new FormatException ("Input string was not in a correct format.");
+
// M = width (non-negative integer)
if (str[ptr] == ',') {
- left_align = (str[++ ptr] == '-');
+ // White space between ',' and number or sign.
+ int start = ++ptr;
+ while (Char.IsWhiteSpace (str [ptr]))
+ ++ptr;
+
+ format = str.Substring (start, ptr - start);
+
+ left_align = (str [ptr] == '-');
if (left_align)
++ ptr;
width = ParseDecimal (str, ref ptr);
if (width < 0)
- throw new FormatException ("Input string was not in correct format.");
+ throw new FormatException ("Input string was not in a correct format.");
}
else {
width = 0;
left_align = false;
+ format = "";
}
// F = argument format (string)
while (str[ptr] != '}')
++ ptr;
- format = str.Substring (start, ptr - start);
+ format += str.Substring (start, ptr - start);
}
else
format = null;
if (str[ptr ++] != '}')
- throw new FormatException ("Input string was not in correct format.");
+ throw new FormatException ("Input string was not in a correct format.");
}
catch (IndexOutOfRangeException) {
- throw new FormatException ("Input string was not in correct format.");
+ throw new FormatException ("Input string was not in a correct format.");
}
}
- private static int ParseDecimal (string str, ref int ptr) {
+ private static int ParseDecimal (string str, ref int ptr)
+ {
int p = ptr;
int n = 0;
while (true) {
if (p == ptr)
return -1;
-
+
ptr = p;
return n;
}
+
+ internal unsafe void InternalSetChar (int idx, char val)
+ {
+ if ((uint) idx >= (uint) Length)
+ throw new ArgumentOutOfRangeException ("idx");
+
+ fixed (char * pStr = &start_char)
+ {
+ pStr [idx] = val;
+ }
+ }
+
+ internal unsafe void InternalSetLength (int newLength)
+ {
+ if (newLength > length)
+ throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
+
+ length = newLength;
+
+ // zero terminate, we can pass string objects directly via pinvoke
+ fixed (char * pStr = &start_char) {
+ pStr [length] = '\0';
+ }
+ }
+
+ [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+ unsafe public extern String (char *value);
+
+ [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+ unsafe public extern String (char *value, int startIndex, int length);
+
+ [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+ unsafe public extern String (sbyte *value);
+
+ [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+ unsafe public extern String (sbyte *value, int startIndex, int length);
+
+ [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
+ unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ public extern String (char [] val, int startIndex, int length);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ public extern String (char [] val);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ public extern String (char c, int count);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ public extern override int GetHashCode ();
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalInsert (int sourceIndex, String value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalReplace (char oldChar, char newChar);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalRemove (int sIndex, int count);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String[] InternalSplit (char[] separator, int count);
+
+ [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);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalPad (int width, char chr, bool right);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalToLower (CultureInfo culture);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern String InternalToUpper (CultureInfo culture);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ internal extern static String InternalAllocateStr (int length);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ internal extern static void InternalStrcpy (String dest, int destPos, String src);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern static string InternalIntern (string str);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern static string InternalIsInterned (string str);
}
}