1 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
6 // Jeffrey Stedfast (fejj@ximian.com)
8 // (C) 2001 Ximian, Inc. http://www.ximian.com
11 // FIXME: from what I gather from msdn, when a function is to return an empty string
12 // we should be returning this.Empty - some methods do this and others don't.
14 // FIXME: I didn't realise until later that `string' has a .Length method and so
15 // I am missing some proper bounds-checking in some methods. Find these
16 // instances and throw the ArgumentOutOfBoundsException at the programmer.
17 // I like pelting programmers with ArgumentOutOfBoundsException's :-)
19 // FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
20 // need to be made unicode aware.
22 // FIXME: when you have a char carr[], does carr.Length include the terminating null char?
26 using System.Collections;
27 using System.Globalization;
28 using System.Runtime.CompilerServices;
32 //[DefaultMemberName("Chars")]
33 public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
34 public static readonly string Empty = "";
40 internal String (int storage)
43 c_str = new char [storage];
47 unsafe public String (char *value)
51 // FIXME: can I do value.Length here?
55 for (i = 0; *(value + i) != '\0'; i++);
59 this.c_str = new char [this.length + 1];
60 for (i = 0; i < this.length; i++)
61 this.c_str[i] = *(value + i);
64 public String (char[] value)
68 // FIXME: value.Length includes the terminating null char?
69 this.length = value != null ? strlen (value): 0;
70 this.c_str = new char [this.length + 1];
71 for (i = 0; i < this.length; i++)
72 this.c_str[i] = value[i];
76 unsafe public String (sbyte *value)
78 // FIXME: consider unicode?
81 // FIXME: can I do value.Length here? */
85 for (i = 0; *(value + i) != '\0'; i++);
89 this.c_str = new char [this.length + 1];
90 for (i = 0; i < this.length; i++)
91 this.c_str[i] = (char) *(value + i);
94 public String (char c, int count)
99 this.c_str = new char [count + 1];
100 for (i = 0; i < count; i++)
104 [CLSCompliant(false)]
105 unsafe public String (char *value, int startIndex, int length)
109 if (value == null && startIndex != 0 && length != 0)
110 throw new ArgumentNullException ();
112 if (startIndex < 0 || length < 0)
113 throw new ArgumentOutOfRangeException ();
115 this.length = length;
116 this.c_str = new char [length + 1];
117 for (i = 0; i < length; i++)
118 this.c_str[i] = *(value + startIndex + i);
121 public String (char[] value, int startIndex, int length)
125 if (value == null && startIndex != 0 && length != 0)
126 throw new ArgumentNullException ();
128 if (startIndex < 0 || length < 0)
129 throw new ArgumentOutOfRangeException ();
131 this.length = length;
132 this.c_str = new char [length + 1];
133 for (i = 0; i < length; i++)
134 this.c_str[i] = value[startIndex + i];
137 [CLSCompliant(false)]
138 unsafe public String (sbyte *value, int startIndex, int length)
140 // FIXME: consider unicode?
143 if (value == null && startIndex != 0 && length != 0)
144 throw new ArgumentNullException ();
146 if (startIndex < 0 || length < 0)
147 throw new ArgumentOutOfRangeException ();
149 this.length = length;
150 this.c_str = new char [length + 1];
151 for (i = 0; i < length; i++)
152 this.c_str[i] = (char) *(value + startIndex + i);
155 [CLSCompliant(false)][MonoTODO]
156 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
158 // FIXME: implement me
163 // FIXME: is there anything we need to do here?
164 /*base.Finalize ();*/
174 [IndexerName("Chars")]
175 public char this [int index] {
177 if (index >= this.length)
178 throw new ArgumentOutOfRangeException ();
180 return this.c_str[index];
184 // Private helper methods
185 private static int strlen (char[] str)
187 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
192 private static char tolowerordinal (char c)
194 // FIXME: implement me
198 private static bool is_lwsp (char c)
200 /* this comes from the msdn docs for String.Trim() */
201 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
202 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
208 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
210 /* (hopefully) Unicode-safe Boyer-Moore implementation */
211 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
212 int h, n, he, ne, hc, nc, i;
214 if (haystack == null || needle == null)
215 throw new ArgumentNullException ();
217 /* if the search buffer is shorter than the pattern buffer, we can't match */
218 if (count < needle.length)
221 /* return an instant match if the pattern is 0-length */
222 if (needle.length == 0)
225 /* set a pointer at the end of each string */
226 ne = needle.length - 1; /* position of char before '\0' */
227 he = startIndex + count; /* position of last valid char */
229 /* init the skip table with the pattern length */
231 for (i = 0; i < 65536; i++)
234 /* set the skip value for the chars that *do* appear in the
235 * pattern buffer (needle) to the distance from the index to
236 * the end of the pattern buffer. */
237 for (nc = 0; nc < ne; nc++)
238 skiptable[(int) needle[nc]] = ne - nc;
241 while (count >= needle.length) {
242 hc = h + needle.length - 1; /* set the haystack compare pointer */
243 nc = ne; /* set the needle compare pointer */
245 /* work our way backwards until they don't match */
246 for (i = 0; nc > 0; nc--, hc--, i++)
247 if (needle[nc] != haystack[hc])
250 if (needle[nc] != haystack[hc]) {
251 n = skiptable[(int) haystack[hc]] - i;
263 public object Clone ()
265 // FIXME: implement me
269 public static int Compare (string strA, string strB)
278 } else if (strB == null)
281 min = strA.length < strB.length ? strA.length : strB.length;
283 for (i = 0; i < min; i++) {
284 if (strA.c_str[i] != strB.c_str[i]) {
285 return (int)strA.c_str[i] - (int)strB.c_str[i];
288 if (strA.length == strB.length) {
291 if (strA.length > min) {
298 public static int Compare (string strA, string strB, bool ignoreCase)
303 return Compare (strA, strB);
306 * And here I thought Eazel developers were on crack...
307 * if a string is null it should pelt the programmer with
308 * ArgumentNullExceptions, damnit!
315 } else if (strB == null)
318 min = strA.length < strB.length ? strA.length : strB.length;
320 for (i = 0; i < min; i++) {
321 if (Char.ToLower (strA.c_str[i]) != Char.ToLower (strB.c_str[i])) {
322 return (int)strA.c_str[i] - (int)strB.c_str[i];
325 if (strA.length == strB.length) {
328 if (strA.length > min) {
336 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
338 // FIXME: implement me
342 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
346 if (length < 0 || indexA < 0 || indexB < 0)
347 throw new ArgumentOutOfRangeException ();
349 if (indexA > strA.Length || indexB > strB.Length)
350 throw new ArgumentOutOfRangeException ();
352 /* And again with the ("" > null) logic... lord have mercy! */
358 } else if (strB == null)
361 for (i = 0; i < length - 1; i++) {
362 if (strA[indexA + i] != strB[indexB + i])
366 return ((int) (strA[indexA + i] - strB[indexB + i]));
369 public static int Compare (string strA, int indexA, string strB, int indexB,
370 int length, bool ignoreCase)
375 return Compare (strA, indexA, strB, indexB, length);
377 if (length < 0 || indexA < 0 || indexB < 0)
378 throw new ArgumentOutOfRangeException ();
380 if (indexA > strA.Length || indexB > strB.Length)
381 throw new ArgumentOutOfRangeException ();
383 /* When will the hurting stop!?!? */
389 } else if (strB == null)
392 for (i = 0; i < length - 1; i++) {
393 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
397 return ((int) (strA[indexA + i] - strB[indexB + i]));
401 public static int Compare (string strA, int indexA, string strB, int indexB,
402 int length, bool ignoreCase, CultureInfo culture)
405 throw new ArgumentNullException ();
407 if (length < 0 || indexA < 0 || indexB < 0)
408 throw new ArgumentOutOfRangeException ();
410 if (indexA > strA.Length || indexB > strB.Length)
411 throw new ArgumentOutOfRangeException ();
413 /* I can't take it anymore! */
419 } else if (strB == null)
422 // FIXME: implement me
426 public static int CompareOrdinal (string strA, string strB)
430 /* Please God, make it stop! */
436 } else if (strB == null)
439 for (i = 0; i < strA.Length && i < strB.Length; i++) {
442 cA = tolowerordinal (strA[i]);
443 cB = tolowerordinal (strB[i]);
449 return ((int) (strA[i] - strB[i]));
452 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
457 if (length < 0 || indexA < 0 || indexB < 0)
458 throw new ArgumentOutOfRangeException ();
465 } else if (strB == null)
468 for (i = 0; i < length; i++) {
471 cA = tolowerordinal (strA[indexA + i]);
472 cB = tolowerordinal (strB[indexB + i]);
478 return ((int) (strA[indexA + i] - strB[indexB + i]));
481 public int CompareTo (object obj)
483 return Compare (this, obj == null ? null : obj.ToString ());
486 public int CompareTo (string str)
488 return Compare (this, str);
491 public static string Concat (object arg)
493 return arg != null ? arg.ToString () : String.Empty;
496 public static string Concat (params object[] args)
503 throw new ArgumentNullException ();
505 strings = new string [args.Length];
508 foreach (object arg in args) {
509 /* use Empty for each null argument */
511 strings[i] = String.Empty;
513 strings[i] = arg.ToString ();
514 len += strings[i].length;
521 String res = new String (len);
524 for (int j = 0; j < strings.Length; j++)
525 for (int k = 0; k < strings[j].length; k++)
526 str[i++] = strings[j].c_str[k];
531 public static string Concat (params string[] values)
537 throw new ArgumentNullException ();
540 foreach (string value in values)
541 len += value != null ? value.Length : 0;
546 String res = new String (len);
549 foreach (string value in values) {
553 for (int j = 0; j < value.length; j++)
554 str[i++] = value.c_str[j];
560 public static string Concat (object arg0, object arg1)
562 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
563 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
565 return Concat (str0, str1);
568 public static string Concat (string str0, string str1)
578 len = str0.length + str1.length;
582 String res = new String (len);
585 for (i = 0; i < str0.length; i++)
586 concat[i] = str0.c_str[i];
587 for (j = 0 ; j < str1.length; j++)
588 concat[i + j] = str1.c_str[j];
593 public static string Concat (object arg0, object arg1, object arg2)
595 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
596 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
597 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
599 return Concat (str0, str1, str2);
602 public static string Concat (string str0, string str1, string str2)
614 len = str0.length + str1.length + str2.length;
618 String res = new String (len);
621 for (i = 0; i < str0.length; i++)
622 concat[i] = str0.c_str[i];
623 for (j = 0; j < str1.length; j++)
624 concat[i + j] = str1.c_str[j];
625 for (k = 0; k < str2.length; k++)
626 concat[i + j + k] = str2.c_str[k];
631 public static string Concat (string str0, string str1, string str2, string str3)
645 len = str0.length + str1.length + str2.length + str3.length;
648 String res = new String (len);
651 for (i = 0; i < str0.length; i++)
652 concat[i] = str0.c_str[i];
653 for (j = 0; j < str1.length; j++)
654 concat[i + j] = str1.c_str[j];
655 for (k = 0; k < str2.length; k++)
656 concat[i + j + k] = str2.c_str[k];
657 for (l = 0; l < str3.length; l++)
658 concat[i + j + k + l] = str3.c_str[l];
663 public static string Copy (string str)
665 // FIXME: how do I *copy* a string if I can only have 1 of each?
667 throw new ArgumentNullException ();
672 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
674 // LAMESPEC: should I null-terminate?
677 if (destination == null)
678 throw new ArgumentNullException ();
680 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
681 throw new ArgumentOutOfRangeException ();
683 if (sourceIndex + count > this.length)
684 throw new ArgumentOutOfRangeException ();
686 if (destinationIndex + count > destination.Length)
687 throw new ArgumentOutOfRangeException ();
689 for (i = 0; i < count; i++)
690 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
693 public bool EndsWith (string value)
695 bool endswith = true;
699 throw new ArgumentNullException ();
701 start = this.length - value.length;
705 for (i = start; i < this.length && endswith; i++)
706 endswith = this.c_str[i] == value.c_str[i - start];
711 public override bool Equals (object obj)
713 if (!(obj is String))
716 return this == (String) obj;
719 public bool Equals (string value)
721 return this == value;
724 public static bool Equals (string a, string b)
729 public static string Format (string format, object arg0) {
730 return Format (null, format, new object[] { arg0 });
733 public static string Format (string format, object arg0, object arg1) {
734 return Format (null, format, new object[] { arg0, arg1 });
737 public static string Format (string format, object arg0, object arg1, object arg2) {
738 return Format (null, format, new object[] { arg0, arg1, arg2 });
741 public static string Format (string format, params object[] args) {
742 return Format (null, format, args);
745 public static string Format (IFormatProvider provider, string format, params object[] args) {
746 if (format == null || args == null)
747 throw new ArgumentNullException ();
749 StringBuilder result = new StringBuilder ();
753 while (ptr < format.Length) {
754 char c = format[ptr ++];
757 result.Append (format, start, ptr - start - 1);
765 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
766 if (n >= args.Length)
767 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
771 object arg = args[n];
776 else if (arg is IFormattable)
777 str = ((IFormattable)arg).ToString (arg_format, provider);
779 str = arg.ToString ();
781 // pad formatted string and append to result
783 if (width > str.Length) {
784 string pad = new String (' ', width - str.Length);
802 if (start < format.Length)
803 result.Append (format.Substring (start));
805 return result.ToString ();
808 public CharEnumerator GetEnumerator ()
810 return new CharEnumerator (this);
813 IEnumerator IEnumerable.GetEnumerator ()
815 return new CharEnumerator (this);
818 public override int GetHashCode ()
822 for (i = 0; i < length; ++i)
823 h = (h << 5) - h + c_str [i];
827 public TypeCode GetTypeCode ()
829 return TypeCode.String;
832 public int IndexOf (char value)
834 return IndexOf (value, 0, this.length);
837 public int IndexOf (string value)
839 return IndexOf (value, 0, this.length);
842 public int IndexOf (char value, int startIndex)
844 return IndexOf (value, startIndex, this.length - startIndex);
847 public int IndexOf (string value, int startIndex)
849 return IndexOf (value, startIndex, this.length - startIndex);
852 public int IndexOf (char value, int startIndex, int count)
856 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
857 throw new ArgumentOutOfRangeException ();
859 for (i = startIndex; i - startIndex < count; i++)
860 if (this.c_str[i] == value)
866 public int IndexOf (string value, int startIndex, int count)
869 throw new ArgumentNullException ();
871 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
872 throw new ArgumentOutOfRangeException ();
874 return BoyerMoore (this.c_str, value, startIndex, count);
877 for (i = startIndex; i - startIndex + value.Length <= count; ) {
878 if (this.c_str[i] == value[0]) {
882 for (j = 1; equal && j < value.Length; j++) {
883 equal = this.c_str[i + j] == value[j];
884 if (this.c_str[i + j] == value[0] && nexti == 0)
903 public int IndexOfAny (char[] values)
905 return IndexOfAny (values, 0, this.length);
908 public int IndexOfAny (char[] values, int startIndex)
910 return IndexOfAny (values, startIndex, this.length - startIndex);
913 public int IndexOfAny (char[] values, int startIndex, int count)
916 throw new ArgumentNullException ();
918 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
919 throw new ArgumentOutOfRangeException ();
921 for (int i = startIndex; i < startIndex + count; i++) {
922 for (int j = 0; j < strlen (values); j++) {
923 if (this.c_str[i] == values[j])
931 public string Insert (int startIndex, string value)
937 throw new ArgumentNullException ();
939 if (startIndex < 0 || startIndex > this.length)
940 throw new ArgumentOutOfRangeException ();
942 String res = new String (value.length + this.length);
945 for (i = 0; i < startIndex; i++)
946 str[i] = this.c_str[i];
947 for (j = 0; j < value.length; j++)
948 str[i + j] = value.c_str[j];
949 for ( ; i < this.length; i++)
950 str[i + j] = this.c_str[i];
955 [MethodImplAttribute(MethodImplOptions.InternalCall)]
956 public extern static string Intern (string str);
958 [MethodImplAttribute(MethodImplOptions.InternalCall)]
959 public extern static string IsInterned (string str);
961 public static string Join (string separator, string[] value)
963 return Join (separator, value, 0, value.Length);
966 public static string Join (string separator, string[] value, int startIndex, int count)
968 // LAMESPEC: msdn doesn't specify what happens when separator is null
972 if (separator == null || value == null)
973 throw new ArgumentNullException ();
975 if (startIndex + count > value.Length)
976 throw new ArgumentOutOfRangeException ();
979 for (i = startIndex, used = 0; used < count; i++, used++) {
981 len += separator.length;
983 len += value[i].length;
986 // We have no elements to join?
990 String res = new String (len);
993 for (i = 0; i < value[startIndex].length; i++)
994 str[i] = value[startIndex][i];
997 for (j = startIndex + 1; used < count; j++, used++) {
1000 for (k = 0; k < separator.length; k++)
1001 str[i++] = separator.c_str[k];
1002 for (k = 0; k < value[j].length; k++)
1003 str[i++] = value[j].c_str[k];
1009 public int LastIndexOf (char value)
1011 int i = this.length;
1015 for (; i >= 0; i--) {
1016 if (this.c_str[i] == value)
1023 public int LastIndexOf (string value)
1025 return LastIndexOf (value, this.length - 1, this.length);
1028 public int LastIndexOf (char value, int startIndex)
1030 if (startIndex < 0 || startIndex >= this.length)
1031 throw new ArgumentOutOfRangeException ();
1033 for (int i = startIndex; i >= 0; i--) {
1034 if (this.c_str[i] == value)
1041 public int LastIndexOf (string value, int startIndex)
1043 return LastIndexOf (value, startIndex, startIndex + 1);
1046 public int LastIndexOf (char value, int startIndex, int count)
1048 if (startIndex < 0 || count < 0)
1049 throw new ArgumentOutOfRangeException ();
1051 if (startIndex >= this.length || startIndex - count + 1 < 0)
1052 throw new ArgumentOutOfRangeException ();
1054 for (int i = startIndex; i > startIndex - count; i--) {
1055 if (this.c_str[i] == value)
1062 public int LastIndexOf (string value, int startIndex, int count)
1064 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
1065 // but maybe it's the end-position in MS's implementation?
1066 // msdn is unclear on this point. I think this is correct though.
1070 throw new ArgumentNullException ();
1072 if (startIndex < 0 || startIndex >= this.length)
1073 throw new ArgumentOutOfRangeException ();
1075 if (count < 0 || startIndex - count + 1 < 0)
1076 throw new ArgumentOutOfRangeException ();
1078 if (value == String.Empty)
1081 if (startIndex + value.length > this.length) {
1082 /* just a little optimization */
1085 start = this.length - value.length;
1086 count -= startIndex - start;
1090 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1091 len = value.length - 1;
1092 for (i = startIndex; i > startIndex - count; i--) {
1093 if (this.c_str[i + len] == value.c_str[len]) {
1097 for (j = len - 1; equal && j >= 0; j--)
1098 equal = this.c_str[i + j] == value.c_str[j];
1108 public int LastIndexOfAny (char[] values)
1110 return LastIndexOfAny (values, this.length - 1, this.length);
1113 public int LastIndexOfAny (char[] values, int startIndex)
1115 return LastIndexOfAny (values, startIndex, startIndex + 1);
1118 public int LastIndexOfAny (char[] values, int startIndex, int count)
1123 throw new ArgumentNullException ();
1125 if (startIndex < 0 || count < 0 || startIndex - count + 1 < 0)
1126 throw new ArgumentOutOfRangeException ();
1128 for (i = startIndex; i > startIndex - count; i--) {
1129 for (int j = 0; j < strlen (values); j++) {
1130 if (this.c_str[i] == values[j])
1138 public string PadLeft (int totalWidth)
1140 return PadLeft (totalWidth, ' ');
1143 public string PadLeft (int totalWidth, char padChar)
1149 throw new ArgumentException ();
1151 str = new char [totalWidth > this.length ? totalWidth : this.length];
1152 for (i = 0; i < totalWidth - this.length; i++)
1155 for (j = 0; j < this.length; i++, j++)
1156 str[i] = this.c_str[j];
1158 return new String (str);
1161 public string PadRight (int totalWidth)
1163 return PadRight (totalWidth, ' ');
1166 public string PadRight (int totalWidth, char padChar)
1172 throw new ArgumentException ();
1174 str = new char [totalWidth > this.length ? totalWidth : this.length];
1175 for (i = 0; i < this.length; i++)
1176 str[i] = this.c_str[i];
1178 for ( ; i < str.Length; i++)
1181 return new String (str);
1184 public string Remove (int startIndex, int count)
1189 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1190 throw new ArgumentOutOfRangeException ();
1192 len = this.length - count;
1194 return String.Empty;
1196 String res = new String (len);
1198 for (i = 0; i < startIndex; i++)
1199 str[i] = this.c_str[i];
1200 for (j = i + count; j < this.length; j++)
1201 str[i++] = this.c_str[j];
1206 public string Replace (char oldChar, char newChar)
1211 String res = new String (length);
1213 for (i = 0; i < this.length; i++) {
1214 if (this.c_str[i] == oldChar)
1217 str[i] = this.c_str[i];
1223 public string Replace (string oldValue, string newValue)
1225 // LAMESPEC: msdn doesn't specify what to do if either args is null
1226 int index, len, i, j;
1229 if (oldValue == null || newValue == null)
1230 throw new ArgumentNullException ();
1232 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1233 index = IndexOf (oldValue, 0);
1235 // This is the easy one ;-)
1236 return Substring (0, this.length);
1239 len = this.length - oldValue.length + newValue.length;
1241 return String.Empty;
1243 String res = new String (len);
1245 for (i = 0; i < index; i++)
1246 str[i] = this.c_str[i];
1247 for (j = 0; j < newValue.length; j++)
1248 str[i++] = newValue[j];
1249 for (j = index + oldValue.length; j < this.length; j++)
1250 str[i++] = this.c_str[j];
1255 private int splitme (char[] separators, int startIndex)
1257 /* this is basically a customized IndexOfAny() for the Split() methods */
1258 for (int i = startIndex; i < this.length; i++) {
1259 if (separators != null) {
1260 foreach (char sep in separators) {
1261 if (this.c_str[i] == sep)
1262 return i - startIndex;
1264 } else if (is_lwsp (this.c_str[i])) {
1265 return i - startIndex;
1272 public string[] Split (params char[] separator)
1276 * @separator: delimiting chars or null to split on whtspc
1278 * Returns: 1. An array consisting of a single
1279 * element (@this) if none of the delimiting
1280 * chars appear in @this. 2. An array of
1281 * substrings which are delimited by one of
1282 * the separator chars. 3. An array of
1283 * substrings separated by whitespace if
1284 * @separator is null. The Empty string should
1285 * be returned wherever 2 delimiting chars are
1288 // FIXME: would using a Queue be better?
1293 list = new ArrayList ();
1294 for (index = 0, len = 0; index < this.length; index += len + 1) {
1295 len = splitme (separator, index);
1296 len = len > -1 ? len : this.length - index;
1298 list.Add (String.Empty);
1303 str = new char [len];
1304 for (i = 0; i < len; i++)
1305 str[i] = this.c_str[index + i];
1307 list.Add (new String (str));
1311 strings = new string [list.Count];
1312 if (list.Count == 1) {
1313 /* special case for an array holding @this */
1316 for (index = 0; index < list.Count; index++)
1317 strings[index] = (string) list[index];
1323 public string[] Split (char[] separator, int maxCount)
1325 // FIXME: what to do if maxCount <= 0?
1326 // FIXME: would using Queue be better than ArrayList?
1329 int index, len, used;
1332 list = new ArrayList ();
1333 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1334 len = splitme (separator, index);
1335 len = len > -1 ? len : this.length - index;
1337 list.Add (String.Empty);
1342 str = new char [len];
1343 for (i = 0; i < len; i++)
1344 str[i] = this.c_str[index + i];
1346 list.Add (new String (str));
1351 /* fit the remaining chunk of the @this into it's own element */
1352 if (index != this.length) {
1356 str = new char [this.length - index];
1357 for (i = index; i < this.length; i++)
1358 str[i - index] = this.c_str[i];
1360 list.Add (new String (str));
1363 strings = new string [list.Count];
1364 if (list.Count == 1) {
1365 /* special case for an array holding @this */
1368 for (index = 0; index < list.Count; index++)
1369 strings[index] = (string) list[index];
1375 public bool StartsWith (string value)
1377 bool startswith = true;
1381 throw new ArgumentNullException ();
1383 if (value.length > this.length)
1386 for (i = 0; i < value.length && startswith; i++)
1387 startswith = startswith && value.c_str[i] == this.c_str[i];
1392 public string Substring (int startIndex)
1397 if (startIndex < 0 || startIndex > this.length)
1398 throw new ArgumentOutOfRangeException ();
1400 len = this.length - startIndex;
1402 return String.Empty;
1403 String res = new String (len);
1405 for (i = startIndex; i < this.length; i++)
1406 str[i - startIndex] = this.c_str[i];
1411 public string Substring (int startIndex, int length)
1416 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1417 throw new ArgumentOutOfRangeException ();
1420 return String.Empty;
1422 String res = new String (length);
1424 for (i = startIndex; i < startIndex + length; i++)
1425 str[i - startIndex] = this.c_str[i];
1431 public bool ToBoolean (IFormatProvider provider)
1433 // FIXME: implement me
1434 throw new NotImplementedException ();
1438 public byte ToByte (IFormatProvider provider)
1440 // FIXME: implement me
1441 throw new NotImplementedException ();
1445 public char ToChar (IFormatProvider provider)
1447 // FIXME: implement me
1448 throw new NotImplementedException ();
1451 public char[] ToCharArray ()
1453 return ToCharArray (0, this.length);
1456 public char[] ToCharArray (int startIndex, int length)
1461 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1462 throw new ArgumentOutOfRangeException ();
1464 chars = new char [length];
1465 for (i = startIndex; i < length; i++)
1466 chars[i - startIndex] = this.c_str[i];
1472 public DateTime ToDateTime (IFormatProvider provider)
1474 // FIXME: implement me
1475 // return new DateTime (0);
1476 throw new NotImplementedException ();
1480 public decimal ToDecimal (IFormatProvider provider)
1482 // FIXME: implement me
1483 throw new NotImplementedException ();
1487 public double ToDouble (IFormatProvider provider)
1489 // FIXME: implement me
1490 throw new NotImplementedException ();
1494 public short ToInt16 (IFormatProvider provider)
1496 // FIXME: implement me
1497 throw new NotImplementedException ();
1501 public int ToInt32 (IFormatProvider provider)
1503 // FIXME: implement me
1504 throw new NotImplementedException ();
1508 public long ToInt64 (IFormatProvider provider)
1510 // FIXME: implement me
1511 throw new NotImplementedException ();
1514 public string ToLower ()
1519 String res = new String (length);
1521 for (i = 0; i < this.length; i++)
1522 str[i] = Char.ToLower (this.c_str[i]);
1528 public string ToLower (CultureInfo culture)
1530 // FIXME: implement me
1531 throw new NotImplementedException ();
1535 [CLSCompliant(false)][MonoTODO]
1536 public sbyte ToSByte (IFormatProvider provider)
1538 // FIXME: implement me
1539 throw new NotImplementedException ();
1543 public float ToSingle (IFormatProvider provider)
1545 // FIXME: implement me
1546 throw new NotImplementedException ();
1549 public override string ToString ()
1555 public string ToString (IFormatProvider format)
1557 // FIXME: implement me
1558 throw new NotImplementedException ();
1562 public object ToType (Type conversionType, IFormatProvider provider)
1564 // FIXME: implement me
1565 throw new NotImplementedException ();
1568 [CLSCompliant(false)][MonoTODO]
1569 public ushort ToUInt16 (IFormatProvider provider)
1571 // FIXME: implement me
1572 throw new NotImplementedException ();
1575 [CLSCompliant(false)][MonoTODO]
1576 public uint ToUInt32 (IFormatProvider provider)
1578 // FIXME: implement me
1579 throw new NotImplementedException ();
1582 [CLSCompliant(false)][MonoTODO]
1583 public ulong ToUInt64 (IFormatProvider provider)
1585 // FIXME: implement me
1586 throw new NotImplementedException ();
1589 public string ToUpper ()
1594 String res = new String (length);
1596 for (i = 0; i < this.length; i++)
1597 str[i] = Char.ToUpper (this.c_str[i]);
1603 public string ToUpper (CultureInfo culture)
1605 // FIXME: implement me
1606 throw new NotImplementedException ();
1609 public string Trim ()
1614 public string Trim (params char[] trimChars)
1620 for (begin = 0; matches && begin < this.length; begin++) {
1621 if (trimChars != null) {
1623 foreach (char c in trimChars) {
1624 matches = this.c_str[begin] == c;
1629 matches = is_lwsp (this.c_str[begin]);
1634 for (end = this.length - 1; matches && end > begin; end--) {
1635 if (trimChars != null) {
1637 foreach (char c in trimChars) {
1638 matches = this.c_str[end] == c;
1643 matches = is_lwsp (this.c_str[end]);
1648 return String.Empty;
1650 return Substring (begin, end - begin);
1653 public string TrimEnd (params char[] trimChars)
1655 bool matches = true;
1658 for (end = this.length; end > 0; end--) {
1659 if (trimChars != null) {
1661 foreach (char c in trimChars) {
1662 matches = this.c_str[end] == c;
1667 matches = is_lwsp (this.c_str[end]);
1672 return String.Empty;
1674 return Substring (0, end);
1677 public string TrimStart (params char[] trimChars)
1679 bool matches = true;
1682 for (begin = 0; matches && begin < this.length; begin++) {
1683 if (trimChars != null) {
1685 foreach (char c in trimChars) {
1686 matches = this.c_str[begin] == c;
1691 matches = is_lwsp (this.c_str[begin]);
1695 if (begin == this.length)
1696 return String.Empty;
1698 return Substring (begin, this.length - begin);
1702 public static bool operator ==(string a, string b)
1704 if ((object)a == null) {
1705 if ((object)b == null)
1709 if ((object)b == null)
1712 if (a.length != b.length)
1716 for (int i = 0; i < l; i++)
1717 if (a.c_str[i] != b.c_str[i])
1723 public static bool operator !=(string a, string b)
1730 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
1731 // parses format specifier of form:
1737 // N = argument number (non-negative integer)
1739 n = ParseDecimal (str, ref ptr);
1741 throw new FormatException ("Input string was not in correct format.");
1743 // M = width (non-negative integer)
1745 if (str[ptr] == ',') {
1746 left_align = (str[++ ptr] == '-');
1750 width = ParseDecimal (str, ref ptr);
1752 throw new FormatException ("Input string was not in correct format.");
1759 // F = argument format (string)
1761 if (str[ptr] == ':') {
1763 while (str[ptr] != '}')
1766 format = str.Substring (start, ptr - start);
1771 if (str[ptr ++] != '}')
1772 throw new FormatException ("Input string was not in correct format.");
1774 catch (IndexOutOfRangeException) {
1775 throw new FormatException ("Input string was not in correct format.");
1779 private static int ParseDecimal (string str, ref int ptr) {
1784 if (c < '0' || '9' < c)
1787 n = n * 10 + c - '0';