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 public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
33 public static readonly string Empty = "";
39 internal String (int storage)
42 c_str = new char [storage];
46 unsafe public String (char *value)
50 // FIXME: can I do value.Length here?
54 for (i = 0; *(value + i) != '\0'; i++);
58 this.c_str = new char [this.length + 1];
59 for (i = 0; i < this.length; i++)
60 this.c_str[i] = *(value + i);
63 public String (char[] value)
67 // FIXME: value.Length includes the terminating null char?
68 this.length = value != null ? strlen (value): 0;
69 this.c_str = new char [this.length + 1];
70 for (i = 0; i < this.length; i++)
71 this.c_str[i] = value[i];
75 unsafe public String (sbyte *value)
77 // FIXME: consider unicode?
80 // FIXME: can I do value.Length here? */
84 for (i = 0; *(value + i) != '\0'; i++);
88 this.c_str = new char [this.length + 1];
89 for (i = 0; i < this.length; i++)
90 this.c_str[i] = (char) *(value + i);
93 public String (char c, int count)
98 this.c_str = new char [count + 1];
99 for (i = 0; i < count; i++)
103 [CLSCompliant(false)]
104 unsafe public String (char *value, int startIndex, int length)
108 if (value == null && startIndex != 0 && length != 0)
109 throw new ArgumentNullException ();
111 if (startIndex < 0 || length < 0)
112 throw new ArgumentOutOfRangeException ();
114 this.length = length;
115 this.c_str = new char [length + 1];
116 for (i = 0; i < length; i++)
117 this.c_str[i] = *(value + startIndex + i);
120 public String (char[] value, int startIndex, int length)
124 if (value == null && startIndex != 0 && length != 0)
125 throw new ArgumentNullException ();
127 if (startIndex < 0 || length < 0)
128 throw new ArgumentOutOfRangeException ();
130 this.length = length;
131 this.c_str = new char [length + 1];
132 for (i = 0; i < length; i++)
133 this.c_str[i] = value[startIndex + i];
136 [CLSCompliant(false)]
137 unsafe public String (sbyte *value, int startIndex, int length)
139 // FIXME: consider unicode?
142 if (value == null && startIndex != 0 && length != 0)
143 throw new ArgumentNullException ();
145 if (startIndex < 0 || length < 0)
146 throw new ArgumentOutOfRangeException ();
148 this.length = length;
149 this.c_str = new char [length + 1];
150 for (i = 0; i < length; i++)
151 this.c_str[i] = (char) *(value + startIndex + i);
154 [CLSCompliant(false)][TODO]
155 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
157 // FIXME: implement me
162 // FIXME: is there anything we need to do here?
163 /*base.Finalize ();*/
173 // FIXME: is this correct syntax??
174 public char this [int index] {
176 if (index >= this.length)
177 throw new ArgumentOutOfRangeException ();
179 return this.c_str[index];
183 // Private helper methods
184 private static int strlen (char[] str)
186 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
191 private static char tolowerordinal (char c)
193 // FIXME: implement me
197 private static bool is_lwsp (char c)
199 /* this comes from the msdn docs for String.Trim() */
200 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
201 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
207 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
209 /* (hopefully) Unicode-safe Boyer-Moore implementation */
210 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
211 int h, n, he, ne, hc, nc, i;
213 if (haystack == null || needle == null)
214 throw new ArgumentNullException ();
216 /* if the search buffer is shorter than the pattern buffer, we can't match */
217 if (count < needle.Length)
220 /* return an instant match if the pattern is 0-length */
221 if (needle.Length == 0)
224 /* set a pointer at the end of each string */
225 ne = needle.Length - 1; /* position of char before '\0' */
226 he = startIndex + count; /* position of last valid char */
228 /* init the skip table with the pattern length */
229 for (i = 0; i < 65536; i++)
230 skiptable[i] = needle.Length;
232 /* set the skip value for the chars that *do* appear in the
233 * pattern buffer (needle) to the distance from the index to
234 * the end of the pattern buffer. */
235 for (nc = 0; nc < ne; nc++)
236 skiptable[(int) needle[nc]] = ne - nc;
239 while (count >= needle.Length) {
240 hc = h + needle.Length - 1; /* set the haystack compare pointer */
241 nc = ne; /* set the needle compare pointer */
243 /* work our way backwards until they don't match */
244 for (i = 0; nc > 0; nc--, hc--, i++)
245 if (needle[nc] != haystack[hc])
248 if (needle[nc] != haystack[hc]) {
249 n = skiptable[(int) haystack[hc]] - i;
261 public object Clone ()
263 // FIXME: implement me
267 public static int Compare (string strA, string strB)
276 } else if (strB == null)
279 min = strA.length < strB.length ? strA.length : strB.length;
281 for (i = 0; i < min; i++) {
282 if (strA.c_str[i] != strB.c_str[i]) {
283 return (int)strA.c_str[i] - (int)strB.c_str[i];
286 if (strA.length == strB.length) {
289 if (strA.length > min) {
296 public static int Compare (string strA, string strB, bool ignoreCase)
301 return Compare (strA, strB);
304 * And here I thought Eazel developers were on crack...
305 * if a string is null it should pelt the programmer with
306 * ArgumentNullExceptions, damnit!
313 } else if (strB == null)
316 min = strA.length < strB.length ? strA.length : strB.length;
318 for (i = 0; i < min; i++) {
319 if (Char.ToLower (strA.c_str[i]) != Char.ToLower (strB.c_str[i]))
323 return ((int) (strA.c_str[i] - strB.c_str[i]));
327 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
329 // FIXME: implement me
333 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
337 if (length < 0 || indexA < 0 || indexB < 0)
338 throw new ArgumentOutOfRangeException ();
340 if (indexA > strA.Length || indexB > strB.Length)
341 throw new ArgumentOutOfRangeException ();
343 /* And again with the ("" > null) logic... lord have mercy! */
349 } else if (strB == null)
352 for (i = 0; i < length - 1; i++) {
353 if (strA[indexA + i] != strB[indexB + i])
357 return ((int) (strA[indexA + i] - strB[indexB + i]));
360 public static int Compare (string strA, int indexA, string strB, int indexB,
361 int length, bool ignoreCase)
366 return Compare (strA, indexA, strB, indexB, length);
368 if (length < 0 || indexA < 0 || indexB < 0)
369 throw new ArgumentOutOfRangeException ();
371 if (indexA > strA.Length || indexB > strB.Length)
372 throw new ArgumentOutOfRangeException ();
374 /* When will the hurting stop!?!? */
380 } else if (strB == null)
383 for (i = 0; i < length - 1; i++) {
384 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
388 return ((int) (strA[indexA + i] - strB[indexB + i]));
392 public static int Compare (string strA, int indexA, string strB, int indexB,
393 int length, bool ignoreCase, CultureInfo culture)
396 throw new ArgumentNullException ();
398 if (length < 0 || indexA < 0 || indexB < 0)
399 throw new ArgumentOutOfRangeException ();
401 if (indexA > strA.Length || indexB > strB.Length)
402 throw new ArgumentOutOfRangeException ();
404 /* I can't take it anymore! */
410 } else if (strB == null)
413 // FIXME: implement me
417 public static int CompareOrdinal (string strA, string strB)
421 /* Please God, make it stop! */
427 } else if (strB == null)
430 for (i = 0; i < strA.Length && i < strB.Length; i++) {
433 cA = tolowerordinal (strA[i]);
434 cB = tolowerordinal (strB[i]);
440 return ((int) (strA[i] - strB[i]));
443 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
448 if (length < 0 || indexA < 0 || indexB < 0)
449 throw new ArgumentOutOfRangeException ();
456 } else if (strB == null)
459 for (i = 0; i < length; i++) {
462 cA = tolowerordinal (strA[indexA + i]);
463 cB = tolowerordinal (strB[indexB + i]);
469 return ((int) (strA[indexA + i] - strB[indexB + i]));
472 public int CompareTo (object obj)
474 return Compare (this, obj == null ? null : obj.ToString ());
477 public int CompareTo (string str)
479 return Compare (this, str);
482 public static string Concat (object arg)
484 return arg != null ? arg.ToString () : String.Empty;
487 public static string Concat (params object[] args)
494 throw new ArgumentNullException ();
496 strings = new string [args.Length];
499 foreach (object arg in args) {
500 /* use Empty for each null argument */
502 strings[i] = String.Empty;
504 strings[i] = arg.ToString ();
505 len += strings[i].length;
512 String res = new String (len);
515 for (int j = 0; j < strings.Length; j++)
516 for (int k = 0; k < strings[j].length; k++)
517 str[i++] = strings[j].c_str[k];
522 public static string Concat (params string[] values)
528 throw new ArgumentNullException ();
531 foreach (string value in values)
532 len += value != null ? value.Length : 0;
537 String res = new String (len);
540 foreach (string value in values) {
544 for (int j = 0; j < value.length; j++)
545 str[i++] = value.c_str[j];
551 public static string Concat (object arg0, object arg1)
553 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
554 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
556 return Concat (str0, str1);
559 public static string Concat (string str0, string str1)
569 len = str0.length + str1.length;
573 String res = new String (len);
576 for (i = 0; i < str0.length; i++)
577 concat[i] = str0.c_str[i];
578 for (j = 0 ; j < str1.length; j++)
579 concat[i + j] = str1.c_str[j];
584 public static string Concat (object arg0, object arg1, object arg2)
586 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
587 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
588 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
590 return Concat (str0, str1, str2);
593 public static string Concat (string str0, string str1, string str2)
605 len = str0.length + str1.length + str2.length;
609 String res = new String (len);
612 for (i = 0; i < str0.length; i++)
613 concat[i] = str0.c_str[i];
614 for (j = 0; j < str1.length; j++)
615 concat[i + j] = str1.c_str[j];
616 for (k = 0; k < str2.length; k++)
617 concat[i + j + k] = str2.c_str[k];
622 public static string Concat (string str0, string str1, string str2, string str3)
636 len = str0.length + str1.length + str2.length + str3.length;
639 String res = new String (len);
642 for (i = 0; i < str0.length; i++)
643 concat[i] = str0.c_str[i];
644 for (j = 0; j < str1.length; j++)
645 concat[i + j] = str1.c_str[j];
646 for (k = 0; k < str2.length; k++)
647 concat[i + j + k] = str2.c_str[k];
648 for (l = 0; l < str3.length; l++)
649 concat[i + j + k + l] = str3.c_str[l];
654 public static string Copy (string str)
656 // FIXME: how do I *copy* a string if I can only have 1 of each?
658 throw new ArgumentNullException ();
663 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
665 // LAMESPEC: should I null-terminate?
668 if (destination == null)
669 throw new ArgumentNullException ();
671 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
672 throw new ArgumentOutOfRangeException ();
674 if (sourceIndex + count > this.length)
675 throw new ArgumentOutOfRangeException ();
677 if (destinationIndex + count > destination.Length)
678 throw new ArgumentOutOfRangeException ();
680 for (i = 0; i < count; i++)
681 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
684 public bool EndsWith (string value)
686 bool endswith = true;
690 throw new ArgumentNullException ();
692 start = this.length - value.length;
696 for (i = start; i < this.length && endswith; i++)
697 endswith = this.c_str[i] == value.c_str[i - start];
702 public override bool Equals (object obj)
704 if (!(obj is String))
707 return this == (String) obj;
710 public bool Equals (string value)
712 return this == value;
715 public static bool Equals (string a, string b)
721 public static string Format (string format, object arg0)
723 // FIXME: implement me
728 public static string Format (string format, params object[] args)
730 // FIXME: implement me
735 public static string Format (IFormatProvider provider, string format, params object[] args)
737 // FIXME: implement me
742 public static string Format (string format, object arg0, object arg1)
744 // FIXME: implement me
749 public static string Format (string format, object arg0, object arg1, object arg2)
751 // FIXME: implement me
755 //public CharEnumerator GetEnumerator ()
757 public IEnumerator GetEnumerator ()
759 // FIXME: implement me
763 public override int GetHashCode ()
767 for (i = 0; i < length; ++i)
768 h = (h << 5) - h + c_str [i];
772 public TypeCode GetTypeCode ()
774 return TypeCode.String;
777 public int IndexOf (char value)
779 return IndexOf (value, 0, this.length);
782 public int IndexOf (string value)
784 return IndexOf (value, 0, this.length);
787 public int IndexOf (char value, int startIndex)
789 return IndexOf (value, startIndex, this.length - startIndex);
792 public int IndexOf (string value, int startIndex)
794 return IndexOf (value, startIndex, this.length - startIndex);
797 public int IndexOf (char value, int startIndex, int count)
801 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
802 throw new ArgumentOutOfRangeException ();
804 for (i = startIndex; i - startIndex < count; i++)
805 if (this.c_str[i] == value)
811 public int IndexOf (string value, int startIndex, int count)
814 throw new ArgumentNullException ();
816 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
817 throw new ArgumentOutOfRangeException ();
819 return BoyerMoore (this.c_str, value, startIndex, count);
822 for (i = startIndex; i - startIndex + value.Length <= count; ) {
823 if (this.c_str[i] == value[0]) {
827 for (j = 1; equal && j < value.Length; j++) {
828 equal = this.c_str[i + j] == value[j];
829 if (this.c_str[i + j] == value[0] && nexti == 0)
848 public int IndexOfAny (char[] values)
850 return IndexOfAny (values, 0, this.length);
853 public int IndexOfAny (char[] values, int startIndex)
855 return IndexOfAny (values, startIndex, this.length - startIndex);
858 public int IndexOfAny (char[] values, int startIndex, int count)
861 throw new ArgumentNullException ();
863 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
864 throw new ArgumentOutOfRangeException ();
866 for (int i = startIndex; i < startIndex + count; i++) {
867 for (int j = 0; j < strlen (values); j++) {
868 if (this.c_str[i] == values[j])
876 public string Insert (int startIndex, string value)
882 throw new ArgumentNullException ();
884 if (startIndex < 0 || startIndex > this.length)
885 throw new ArgumentOutOfRangeException ();
887 String res = new String (value.length + this.length);
890 for (i = 0; i < startIndex; i++)
891 str[i] = this.c_str[i];
892 for (j = 0; j < value.length; j++)
893 str[i + j] = value.c_str[j];
894 for ( ; i < this.length; i++)
895 str[i + j] = this.c_str[i];
900 [MethodImplAttribute(MethodImplOptions.InternalCall)]
901 public extern static string Intern (string str);
903 [MethodImplAttribute(MethodImplOptions.InternalCall)]
904 public extern static string IsInterned (string str);
906 public static string Join (string separator, string[] value)
908 return Join (separator, value, 0, value.Length);
911 public static string Join (string separator, string[] value, int startIndex, int count)
913 // LAMESPEC: msdn doesn't specify what happens when separator is null
917 if (separator == null || value == null)
918 throw new ArgumentNullException ();
920 if (startIndex + count > value.Length)
921 throw new ArgumentOutOfRangeException ();
924 for (i = startIndex, used = 0; used < count; i++, used++) {
926 len += separator.length;
928 len += value[i].length;
931 // We have no elements to join?
935 String res = new String (len);
938 for (i = 0; i < value[startIndex].length; i++)
939 str[i] = value[startIndex][i];
942 for (j = startIndex + 1; used < count; j++, used++) {
945 for (k = 0; k < separator.length; k++)
946 str[i++] = separator.c_str[k];
947 for (k = 0; k < value[j].length; k++)
948 str[i++] = value[j].c_str[k];
954 public int LastIndexOf (char value)
960 for (; i >= 0; i--) {
961 if (this.c_str[i] == value)
968 public int LastIndexOf (string value)
970 return LastIndexOf (value, this.length, this.length);
973 public int LastIndexOf (char value, int startIndex)
975 if (startIndex < 0 || startIndex > this.length)
976 throw new ArgumentOutOfRangeException ();
978 for (int i = startIndex; i >= 0; i--) {
979 if (this.c_str[i] == value)
986 public int LastIndexOf (string value, int startIndex)
988 return LastIndexOf (value, startIndex, this.length);
991 public int LastIndexOf (char value, int startIndex, int count)
993 if (startIndex < 0 || count < 0)
994 throw new ArgumentOutOfRangeException ();
996 if (startIndex > this.length || startIndex - count < 0)
997 throw new ArgumentOutOfRangeException ();
999 for (int i = startIndex; i >= startIndex - count; i--) {
1000 if (this.c_str[i] == value)
1007 public int LastIndexOf (string value, int startIndex, int count)
1009 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
1010 // but maybe it's the end-position in MS's implementation?
1011 // msdn is unclear on this point. I think this is correct though.
1015 throw new ArgumentNullException ();
1017 if (startIndex < 0 || startIndex > this.length)
1018 throw new ArgumentOutOfRangeException ();
1020 if (count < 0 || startIndex - count < 0)
1021 throw new ArgumentOutOfRangeException ();
1023 if (value == String.Empty)
1026 if (startIndex + value.length > this.length) {
1027 /* just a little optimization */
1030 start = this.length - value.length;
1031 count -= startIndex - start;
1035 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1036 len = value.length - 1;
1037 for (i = startIndex; i >= startIndex - count; i--) {
1038 if (this.c_str[i + len] == value.c_str[len]) {
1042 for (j = len - 1; equal && j >= 0; j--)
1043 equal = this.c_str[i + j] == value.c_str[j];
1053 public int LastIndexOfAny (char[] values)
1055 return LastIndexOfAny (values, this.length, this.length);
1058 public int LastIndexOfAny (char[] values, int startIndex)
1060 return LastIndexOfAny (values, startIndex, startIndex);
1063 public int LastIndexOfAny (char[] values, int startIndex, int count)
1068 throw new ArgumentNullException ();
1070 if (startIndex < 0 || count < 0 || startIndex - count < 0)
1071 throw new ArgumentOutOfRangeException ();
1073 for (i = startIndex; i >= startIndex - count; i--) {
1074 for (int j = 0; j < strlen (values); j++) {
1075 if (this.c_str[i] == values[j])
1083 public string PadLeft (int totalWidth)
1085 return PadLeft (totalWidth, ' ');
1088 public string PadLeft (int totalWidth, char padChar)
1094 throw new ArgumentException ();
1096 str = new char [totalWidth > this.length ? totalWidth : this.length];
1097 for (i = 0; i < totalWidth - this.length; i++)
1100 for (j = 0; j < this.length; i++, j++)
1101 str[i] = this.c_str[j];
1103 return new String (str);
1106 public string PadRight (int totalWidth)
1108 return PadRight (totalWidth, ' ');
1111 public string PadRight (int totalWidth, char padChar)
1117 throw new ArgumentException ();
1119 str = new char [totalWidth > this.length ? totalWidth : this.length];
1120 for (i = 0; i < this.length; i++)
1121 str[i] = this.c_str[i];
1123 for ( ; i < str.Length; i++)
1126 return new String (str);
1129 public string Remove (int startIndex, int count)
1134 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1135 throw new ArgumentOutOfRangeException ();
1137 len = this.length - count;
1139 return String.Empty;
1141 String res = new String (len);
1143 for (i = 0; i < startIndex; i++)
1144 str[i] = this.c_str[i];
1145 for (j = i + count; j < this.length; j++)
1146 str[i++] = this.c_str[j];
1151 public string Replace (char oldChar, char newChar)
1156 String res = new String (length);
1158 for (i = 0; i < this.length; i++) {
1159 if (this.c_str[i] == oldChar)
1162 str[i] = this.c_str[i];
1168 public string Replace (string oldValue, string newValue)
1170 // LAMESPEC: msdn doesn't specify what to do if either args is null
1171 int index, len, i, j;
1174 if (oldValue == null || newValue == null)
1175 throw new ArgumentNullException ();
1177 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1178 index = IndexOf (oldValue, 0);
1180 // This is the easy one ;-)
1181 return Substring (0, this.length);
1184 len = this.length - oldValue.length + newValue.length;
1186 return String.Empty;
1188 String res = new String (len);
1190 for (i = 0; i < index; i++)
1191 str[i] = this.c_str[i];
1192 for (j = 0; j < newValue.length; j++)
1193 str[i++] = newValue[j];
1194 for (j = index + oldValue.length; j < this.length; j++)
1195 str[i++] = this.c_str[j];
1200 private int splitme (char[] separators, int startIndex)
1202 /* this is basically a customized IndexOfAny() for the Split() methods */
1203 for (int i = startIndex; i < this.length; i++) {
1204 if (separators != null) {
1205 foreach (char sep in separators) {
1206 if (this.c_str[i] == sep)
1207 return i - startIndex;
1209 } else if (is_lwsp (this.c_str[i])) {
1210 return i - startIndex;
1217 public string[] Split (params char[] separator)
1221 * @separator: delimiting chars or null to split on whtspc
1223 * Returns: 1. An array consisting of a single
1224 * element (@this) if none of the delimiting
1225 * chars appear in @this. 2. An array of
1226 * substrings which are delimited by one of
1227 * the separator chars. 3. An array of
1228 * substrings separated by whitespace if
1229 * @separator is null. The Empty string should
1230 * be returned wherever 2 delimiting chars are
1233 // FIXME: would using a Queue be better?
1238 list = new ArrayList ();
1239 for (index = 0, len = 0; index < this.length; index += len + 1) {
1240 len = splitme (separator, index);
1241 len = len > -1 ? len : this.length - index;
1243 list.Add (String.Empty);
1248 str = new char [len];
1249 for (i = 0; i < len; i++)
1250 str[i] = this.c_str[index + i];
1252 list.Add (new String (str));
1256 strings = new string [list.Count];
1257 if (list.Count == 1) {
1258 /* special case for an array holding @this */
1261 for (index = 0; index < list.Count; index++)
1262 strings[index] = (string) list[index];
1268 public string[] Split (char[] separator, int maxCount)
1270 // FIXME: what to do if maxCount <= 0?
1271 // FIXME: would using Queue be better than ArrayList?
1274 int index, len, used;
1277 list = new ArrayList ();
1278 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1279 len = splitme (separator, index);
1280 len = len > -1 ? len : this.length - index;
1282 list.Add (String.Empty);
1287 str = new char [len];
1288 for (i = 0; i < len; i++)
1289 str[i] = this.c_str[index + i];
1291 list.Add (new String (str));
1296 /* fit the remaining chunk of the @this into it's own element */
1297 if (index != this.length) {
1301 str = new char [this.length - index];
1302 for (i = index; i < this.length; i++)
1303 str[i - index] = this.c_str[i];
1305 list.Add (new String (str));
1308 strings = new string [list.Count];
1309 if (list.Count == 1) {
1310 /* special case for an array holding @this */
1313 for (index = 0; index < list.Count; index++)
1314 strings[index] = (string) list[index];
1320 public bool StartsWith (string value)
1322 bool startswith = true;
1326 throw new ArgumentNullException ();
1328 if (value.length > this.length)
1331 for (i = 0; i < value.length && startswith; i++)
1332 startswith = startswith && value.c_str[i] == this.c_str[i];
1337 public string Substring (int startIndex)
1342 if (startIndex < 0 || startIndex > this.length)
1343 throw new ArgumentOutOfRangeException ();
1345 len = this.length - startIndex;
1347 return String.Empty;
1348 String res = new String (len);
1350 for (i = startIndex; i < this.length; i++)
1351 str[i - startIndex] = this.c_str[i];
1356 public string Substring (int startIndex, int length)
1361 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1362 throw new ArgumentOutOfRangeException ();
1365 return String.Empty;
1367 String res = new String (length);
1369 for (i = startIndex; i < startIndex + length; i++)
1370 str[i - startIndex] = this.c_str[i];
1376 public bool ToBoolean (IFormatProvider provider)
1378 // FIXME: implement me
1379 throw new NotImplementedException ();
1383 public byte ToByte (IFormatProvider provider)
1385 // FIXME: implement me
1386 throw new NotImplementedException ();
1390 public char ToChar (IFormatProvider provider)
1392 // FIXME: implement me
1393 throw new NotImplementedException ();
1396 public char[] ToCharArray ()
1398 return ToCharArray (0, this.length);
1401 public char[] ToCharArray (int startIndex, int length)
1406 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1407 throw new ArgumentOutOfRangeException ();
1409 chars = new char [length];
1410 for (i = startIndex; i < length; i++)
1411 chars[i - startIndex] = this.c_str[i];
1417 public DateTime ToDateTime (IFormatProvider provider)
1419 // FIXME: implement me
1420 // return new DateTime (0);
1421 throw new NotImplementedException ();
1425 public decimal ToDecimal (IFormatProvider provider)
1427 // FIXME: implement me
1428 throw new NotImplementedException ();
1432 public double ToDouble (IFormatProvider provider)
1434 // FIXME: implement me
1435 throw new NotImplementedException ();
1439 public short ToInt16 (IFormatProvider provider)
1441 // FIXME: implement me
1442 throw new NotImplementedException ();
1446 public int ToInt32 (IFormatProvider provider)
1448 // FIXME: implement me
1449 throw new NotImplementedException ();
1453 public long ToInt64 (IFormatProvider provider)
1455 // FIXME: implement me
1456 throw new NotImplementedException ();
1459 public string ToLower ()
1464 String res = new String (length);
1466 for (i = 0; i < this.length; i++)
1467 str[i] = Char.ToLower (this.c_str[i]);
1473 public string ToLower (CultureInfo culture)
1475 // FIXME: implement me
1476 throw new NotImplementedException ();
1480 [CLSCompliant(false)][TODO]
1481 public sbyte ToSByte (IFormatProvider provider)
1483 // FIXME: implement me
1484 throw new NotImplementedException ();
1488 public float ToSingle (IFormatProvider provider)
1490 // FIXME: implement me
1491 throw new NotImplementedException ();
1494 public override string ToString ()
1500 public string ToString (IFormatProvider format)
1502 // FIXME: implement me
1503 throw new NotImplementedException ();
1507 public object ToType (Type conversionType, IFormatProvider provider)
1509 // FIXME: implement me
1510 throw new NotImplementedException ();
1513 [CLSCompliant(false)][TODO]
1514 public ushort ToUInt16 (IFormatProvider provider)
1516 // FIXME: implement me
1517 throw new NotImplementedException ();
1520 [CLSCompliant(false)][TODO]
1521 public uint ToUInt32 (IFormatProvider provider)
1523 // FIXME: implement me
1524 throw new NotImplementedException ();
1527 [CLSCompliant(false)][TODO]
1528 public ulong ToUInt64 (IFormatProvider provider)
1530 // FIXME: implement me
1531 throw new NotImplementedException ();
1534 public string ToUpper ()
1539 String res = new String (length);
1541 for (i = 0; i < this.length; i++)
1542 str[i] = Char.ToUpper (this.c_str[i]);
1548 public string ToUpper (CultureInfo culture)
1550 // FIXME: implement me
1551 throw new NotImplementedException ();
1554 public string Trim ()
1559 public string Trim (params char[] trimChars)
1565 for (begin = 0; matches && begin < this.length; begin++) {
1566 if (trimChars != null) {
1568 foreach (char c in trimChars) {
1569 matches = this.c_str[begin] == c;
1574 matches = is_lwsp (this.c_str[begin]);
1579 for (end = this.length-1; end > begin; end--) {
1580 if (trimChars != null) {
1582 foreach (char c in trimChars) {
1583 matches = this.c_str[end] == c;
1588 matches = is_lwsp (this.c_str[end]);
1593 return String.Empty;
1595 return Substring (begin, end - begin);
1598 public string TrimEnd (params char[] trimChars)
1600 bool matches = true;
1603 for (end = this.length; end > 0; end--) {
1604 if (trimChars != null) {
1606 foreach (char c in trimChars) {
1607 matches = this.c_str[end] == c;
1612 matches = is_lwsp (this.c_str[end]);
1617 return String.Empty;
1619 return Substring (0, end);
1622 public string TrimStart (params char[] trimChars)
1624 bool matches = true;
1627 for (begin = 0; matches && begin < this.length; begin++) {
1628 if (trimChars != null) {
1630 foreach (char c in trimChars) {
1631 matches = this.c_str[begin] == c;
1636 matches = is_lwsp (this.c_str[begin]);
1640 if (begin == this.length)
1641 return String.Empty;
1643 return Substring (begin, this.length - begin);
1647 public static bool operator ==(string a, string b)
1649 if (a.length != b.length)
1653 for (int i = 0; i < l; i++)
1654 if (a.c_str[i] != b.c_str[i])
1660 public static bool operator !=(string a, string b)