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 = "";
38 unsafe public String (char *value)
42 // FIXME: can I do value.Length here?
46 for (i = 0; *(value + i) != '\0'; i++);
50 this.c_str = new char [this.length + 1];
51 for (i = 0; i < this.length; i++)
52 this.c_str[i] = *(value + i);
55 public String (char[] value)
59 // FIXME: value.Length includes the terminating null char?
60 this.length = value != null ? strlen (value): 0;
61 this.c_str = new char [this.length + 1];
62 for (i = 0; i < this.length; i++)
63 this.c_str[i] = value[i];
66 unsafe public String (sbyte *value)
68 // FIXME: consider unicode?
71 // FIXME: can I do value.Length here? */
75 for (i = 0; *(value + i) != '\0'; i++);
79 this.c_str = new char [this.length + 1];
80 for (i = 0; i < this.length; i++)
81 this.c_str[i] = (char) *(value + i);
84 public String (char c, int count)
89 this.c_str = new char [count + 1];
90 for (i = 0; i < count; i++)
94 unsafe public String (char *value, int startIndex, int length)
98 if (value == null && startIndex != 0 && length != 0)
99 throw new ArgumentNullException ();
101 if (startIndex < 0 || length < 0)
102 throw new ArgumentOutOfRangeException ();
104 this.length = length;
105 this.c_str = new char [length + 1];
106 for (i = 0; i < length; i++)
107 this.c_str[i] = *(value + startIndex + i);
110 public String (char[] value, int startIndex, int length)
114 if (value == null && startIndex != 0 && length != 0)
115 throw new ArgumentNullException ();
117 if (startIndex < 0 || length < 0)
118 throw new ArgumentOutOfRangeException ();
120 this.length = length;
121 this.c_str = new char [length + 1];
122 for (i = 0; i < length; i++)
123 this.c_str[i] = value[startIndex + i];
126 unsafe public String (sbyte *value, int startIndex, int length)
128 // FIXME: consider unicode?
131 if (value == null && startIndex != 0 && length != 0)
132 throw new ArgumentNullException ();
134 if (startIndex < 0 || length < 0)
135 throw new ArgumentOutOfRangeException ();
137 this.length = length;
138 this.c_str = new char [length + 1];
139 for (i = 0; i < length; i++)
140 this.c_str[i] = (char) *(value + startIndex + i);
143 unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
145 // FIXME: implement me
150 // FIXME: is there anything we need to do here?
151 /*base.Finalize ();*/
161 // FIXME: is this correct syntax??
162 public char this [int index] {
164 if (index >= this.length)
165 throw new ArgumentOutOfRangeException ();
167 return this.c_str[index];
171 // Private helper methods
172 private static int strlen (char[] str)
174 // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
178 private static char tolowerordinal (char c)
180 // FIXME: implement me
184 private static bool is_lwsp (char c)
186 /* this comes from the msdn docs for String.Trim() */
187 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
188 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
194 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
196 /* (hopefully) Unicode-safe Boyer-Moore implementation */
197 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
198 int h, n, he, ne, hc, nc, i;
200 if (haystack == null || needle == null)
201 throw new ArgumentNullException ();
203 /* if the search buffer is shorter than the pattern buffer, we can't match */
204 if (count < needle.Length)
207 /* return an instant match if the pattern is 0-length */
208 if (needle.Length == 0)
211 /* set a pointer at the end of each string */
212 ne = needle.Length - 1; /* position of char before '\0' */
213 he = startIndex + count; /* position of last valid char */
215 /* init the skip table with the pattern length */
216 for (i = 0; i < 65536; i++)
217 skiptable[i] = needle.Length;
219 /* set the skip value for the chars that *do* appear in the
220 * pattern buffer (needle) to the distance from the index to
221 * the end of the pattern buffer. */
222 for (nc = 0; nc < ne; nc++)
223 skiptable[(int) needle[nc]] = ne - nc;
226 while (count >= needle.Length) {
227 hc = h + needle.Length - 1; /* set the haystack compare pointer */
228 nc = ne; /* set the needle compare pointer */
230 /* work our way backwards until they don't match */
231 for (i = 0; nc > 0; nc--, hc--, i++)
232 if (needle[nc] != haystack[hc])
235 if (needle[nc] != haystack[hc]) {
236 n = skiptable[(int) haystack[hc]] - i;
247 public object Clone ()
249 // FIXME: implement me
253 public static int Compare (string strA, string strB)
262 } else if (strB == null)
265 min = strA.Length < strB.Length ? strA.Length : strB.Length;
267 for (i = 0; strA[i] == strB[i] && i < min; i++);
269 return ((int) (strA[i] - strB[i]));
272 public static int Compare (string strA, string strB, bool ignoreCase)
277 return Compare (strA, strB);
280 * And here I thought Eazel developers were on crack...
281 * if a string is null it should pelt the programmer with
282 * ArgumentNullExceptions, damnit!
289 } else if (strB == null)
292 min = strA.Length < strB.Length ? strA.Length : strB.Length;
294 for (i = 0; i < min; i++) {
295 if (Char.ToLower (strA[i]) != Char.ToLower (strB[i]))
299 return ((int) (strA[i] - strB[i]));
302 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
304 // FIXME: implement me
308 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
312 if (length < 0 || indexA < 0 || indexB < 0)
313 throw new ArgumentOutOfRangeException ();
315 if (indexA > strA.Length || indexB > strB.Length)
316 throw new ArgumentOutOfRangeException ();
318 /* And again with the ("" > null) logic... lord have mercy! */
324 } else if (strB == null)
327 for (i = 0; i < length - 1; i++) {
328 if (strA[indexA + i] != strB[indexB + i])
332 return ((int) (strA[indexA + i] - strB[indexB + i]));
335 public static int Compare (string strA, int indexA, string strB, int indexB,
336 int length, bool ignoreCase)
341 return Compare (strA, indexA, strB, indexB, length);
343 if (length < 0 || indexA < 0 || indexB < 0)
344 throw new ArgumentOutOfRangeException ();
346 if (indexA > strA.Length || indexB > strB.Length)
347 throw new ArgumentOutOfRangeException ();
349 /* When will the hurting stop!?!? */
355 } else if (strB == null)
358 for (i = 0; i < length - 1; i++) {
359 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
363 return ((int) (strA[indexA + i] - strB[indexB + i]));
366 public static int Compare (string strA, int indexA, string strB, int indexB,
367 int length, bool ignoreCase, CultureInfo culture)
370 throw new ArgumentNullException ();
372 if (length < 0 || indexA < 0 || indexB < 0)
373 throw new ArgumentOutOfRangeException ();
375 if (indexA > strA.Length || indexB > strB.Length)
376 throw new ArgumentOutOfRangeException ();
378 /* I can't take it anymore! */
384 } else if (strB == null)
387 // FIXME: implement me
391 public static int CompareOrdinal (string strA, string strB)
395 /* Please God, make it stop! */
401 } else if (strB == null)
404 for (i = 0; i < strA.Length && i < strB.Length; i++) {
407 cA = tolowerordinal (strA[i]);
408 cB = tolowerordinal (strB[i]);
414 return ((int) (strA[i] - strB[i]));
417 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
422 if (length < 0 || indexA < 0 || indexB < 0)
423 throw new ArgumentOutOfRangeException ();
430 } else if (strB == null)
433 for (i = 0; i < length; i++) {
436 cA = tolowerordinal (strA[indexA + i]);
437 cB = tolowerordinal (strB[indexB + i]);
443 return ((int) (strA[indexA + i] - strB[indexB + i]));
446 public int CompareTo (object obj)
448 return Compare (this, obj == null ? null : obj.ToString ());
451 public int CompareTo (string str)
453 return Compare (this, str);
456 public static string Concat (object arg)
458 return arg != null ? arg.ToString () : String.Empty;
461 public static string Concat (params object[] args)
468 throw new ArgumentNullException ();
470 strings = new string [args.Length];
473 foreach (object arg in args) {
474 /* use Empty for each null argument */
476 strings[i] = String.Empty;
478 strings[i] = arg.ToString ();
479 len += strings[i].Length;
486 str = new char [len + 1];
488 for (int j = 0; j < strings.Length; j++)
489 for (int k = 0; k < strings[j].Length; k++)
490 str[i++] = strings[j][k];
493 return new String (str);
496 public static string Concat (params string[] values)
502 throw new ArgumentNullException ();
505 foreach (string value in values)
506 len += value != null ? value.Length : 0;
511 str = new char [len + 1];
513 foreach (string value in values) {
517 for (int j = 0; j < value.Length; j++)
522 return new String (str);
525 public static string Concat (object arg0, object arg1)
527 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
528 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
530 return Concat (str0, str1);
533 public static string Concat (string str0, string str1)
543 len = str0.Length + str1.Length;
547 concat = new char [len + 1];
548 for (i = 0; i < str0.Length; i++)
550 for (j = 0 ; j < str1.Length; j++)
551 concat[i + j] = str1[j];
554 return new String (concat);
557 public static string Concat (object arg0, object arg1, object arg2)
559 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
560 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
561 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
563 return Concat (str0, str1, str2);
566 public static string Concat (string str0, string str1, string str2)
578 len = str0.Length + str1.Length + str2.Length;
582 concat = new char [len + 1];
583 for (i = 0; i < str0.Length; i++)
585 for (j = 0; j < str1.Length; j++)
586 concat[i + j] = str1[j];
587 for (k = 0; k < str2.Length; k++)
588 concat[i + j + k] = str2[k];
591 return new String (concat);
594 public static string Concat (string str0, string str1, string str2, string str3)
608 len = str0.Length + str1.Length + str2.Length + str3.Length;
612 concat = new char [len + 1];
613 for (i = 0; i < str0.Length; i++)
615 for (j = 0; j < str1.Length; j++)
616 concat[i + j] = str1[j];
617 for (k = 0; k < str2.Length; k++)
618 concat[i + j + k] = str2[k];
619 for (l = 0; l < str3.Length; l++)
620 concat[i + j + k + l] = str3[l];
623 return new String (concat);
626 public static string Copy (string str)
628 // FIXME: how do I *copy* a string if I can only have 1 of each?
630 throw new ArgumentNullException ();
635 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
637 // LAMESPEC: should I null-terminate?
640 if (destination == null)
641 throw new ArgumentNullException ();
643 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
644 throw new ArgumentOutOfRangeException ();
646 if (sourceIndex + count > this.length)
647 throw new ArgumentOutOfRangeException ();
649 if (destinationIndex + count > destination.Length)
650 throw new ArgumentOutOfRangeException ();
652 for (i = 0; i < count; i++)
653 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
656 public bool EndsWith (string value)
658 bool endswith = true;
662 throw new ArgumentNullException ();
664 start = this.length - value.Length;
668 for (i = start; i < this.length && endswith; i++)
669 endswith = this.c_str[i] == value[i - start];
674 public override bool Equals (object obj)
676 if (!(obj is String))
679 return this == (String) obj;
682 public bool Equals (string value)
684 return this == value;
687 public static bool Equals (string a, string b)
692 public static string Format (string format, object arg0)
694 // FIXME: implement me
698 public static string Format (string format, params object[] args)
700 // FIXME: implement me
704 public static string Format (IFormatProvider provider, string format, params object[] args)
706 // FIXME: implement me
710 public static string Format (string format, object arg0, object arg1)
712 // FIXME: implement me
716 public static string Format (string format, object arg0, object arg1, object arg2)
718 // FIXME: implement me
722 //public CharEnumerator GetEnumerator ()
723 public IEnumerator GetEnumerator ()
725 // FIXME: implement me
729 public override int GetHashCode ()
731 // FIXME: implement me
735 public new Type GetType ()
737 // FIXME: implement me
741 public TypeCode GetTypeCode ()
743 // FIXME: implement me
747 public int IndexOf (char value)
749 return IndexOf (value, 0, this.length);
752 public int IndexOf (string value)
754 return IndexOf (value, 0, this.length);
757 public int IndexOf (char value, int startIndex)
759 return IndexOf (value, startIndex, this.length - startIndex);
762 public int IndexOf (string value, int startIndex)
764 return IndexOf (value, startIndex, this.length - startIndex);
767 public int IndexOf (char value, int startIndex, int count)
771 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
772 throw new ArgumentOutOfRangeException ();
774 for (i = startIndex; i - startIndex < count; i++)
775 if (this.c_str[i] == value)
781 public int IndexOf (string value, int startIndex, int count)
784 throw new ArgumentNullException ();
786 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
787 throw new ArgumentOutOfRangeException ();
789 return BoyerMoore (this.c_str, value, startIndex, count);
792 for (i = startIndex; i - startIndex + value.Length <= count; ) {
793 if (this.c_str[i] == value[0]) {
797 for (j = 1; equal && j < value.Length; j++) {
798 equal = this.c_str[i + j] == value[j];
799 if (this.c_str[i + j] == value[0] && nexti == 0)
818 public int IndexOfAny (char[] values)
820 return IndexOfAny (values, 0, this.length);
823 public int IndexOfAny (char[] values, int startIndex)
825 return IndexOfAny (values, startIndex, this.length - startIndex);
828 public int IndexOfAny (char[] values, int startIndex, int count)
831 throw new ArgumentNullException ();
833 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
834 throw new ArgumentOutOfRangeException ();
836 for (int i = startIndex; i < startIndex + count; i++) {
837 for (int j = 0; j < strlen (values); j++) {
838 if (this.c_str[i] == values[j])
846 public string Insert (int startIndex, string value)
852 throw new ArgumentNullException ();
854 if (startIndex < 0 || startIndex > this.length)
855 throw new ArgumentOutOfRangeException ();
857 str = new char [value.Length + this.length + 1];
858 for (i = 0; i < startIndex; i++)
859 str[i] = this.c_str[i];
860 for (j = 0; j < value.Length; j++)
861 str[i + j] = value[j];
862 for ( ; i < this.length; i++)
863 str[i + j] = this.c_str[i];
866 return new String (str);
869 [MethodImplAttribute(MethodImplOptions.InternalCall)]
870 public extern static string Intern (string str);
872 [MethodImplAttribute(MethodImplOptions.InternalCall)]
873 public extern static string IsInterned (string str);
875 public static string Join (string separator, string[] value)
877 return Join (separator, value, 0, value.Length);
880 public static string Join (string separator, string[] value, int startIndex, int count)
882 // LAMESPEC: msdn doesn't specify what happens when separator is null
886 if (separator == null || value == null)
887 throw new ArgumentNullException ();
889 if (startIndex + count > value.Length)
890 throw new ArgumentOutOfRangeException ();
893 for (i = startIndex, used = 0; used < count; i++, used++) {
895 len += separator.Length;
897 len += value[i].Length;
900 // We have no elements to join?
904 str = new char [len + 1];
905 for (i = 0; i < value[startIndex].Length; i++)
906 str[i] = value[startIndex][i];
909 for (j = startIndex + 1; used < count; j++, used++) {
912 for (k = 0; k < separator.Length; k++)
913 str[i++] = separator[k];
914 for (k = 0; k < value[j].Length; k++)
915 str[i++] = value[j][k];
919 return new String (str);
922 public int LastIndexOf (char value)
928 for (; i >= 0; i--) {
929 if (this.c_str[i] == value)
936 public int LastIndexOf (string value)
938 return LastIndexOf (value, this.length, this.length);
941 public int LastIndexOf (char value, int startIndex)
943 if (startIndex < 0 || startIndex > this.length)
944 throw new ArgumentOutOfRangeException ();
946 for (int i = startIndex; i >= 0; i--) {
947 if (this.c_str[i] == value)
954 public int LastIndexOf (string value, int startIndex)
956 return LastIndexOf (value, startIndex, this.length);
959 public int LastIndexOf (char value, int startIndex, int count)
961 if (startIndex < 0 || count < 0)
962 throw new ArgumentOutOfRangeException ();
964 if (startIndex > this.length || startIndex - count < 0)
965 throw new ArgumentOutOfRangeException ();
967 for (int i = startIndex; i >= startIndex - count; i--) {
968 if (this.c_str[i] == value)
975 public int LastIndexOf (string value, int startIndex, int count)
977 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
978 // but maybe it's the end-position in MS's implementation?
979 // msdn is unclear on this point. I think this is correct though.
983 throw new ArgumentNullException ();
985 if (startIndex < 0 || startIndex > this.length)
986 throw new ArgumentOutOfRangeException ();
988 if (count < 0 || startIndex - count < 0)
989 throw new ArgumentOutOfRangeException ();
991 if (value == String.Empty)
994 if (startIndex + value.Length > this.length) {
995 /* just a little optimization */
998 start = this.length - value.Length;
999 count -= startIndex - start;
1003 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1004 len = value.Length - 1;
1005 for (i = startIndex; i >= startIndex - count; i--) {
1006 if (this.c_str[i + len] == value[len]) {
1010 for (j = len - 1; equal && j >= 0; j--)
1011 equal = this.c_str[i + j] == value[j];
1021 public int LastIndexOfAny (char[] values)
1023 return LastIndexOfAny (values, this.length, this.length);
1026 public int LastIndexOfAny (char[] values, int startIndex)
1028 return LastIndexOfAny (values, startIndex, startIndex);
1031 public int LastIndexOfAny (char[] values, int startIndex, int count)
1036 throw new ArgumentNullException ();
1038 if (startIndex < 0 || count < 0 || startIndex - count < 0)
1039 throw new ArgumentOutOfRangeException ();
1041 for (i = startIndex; i >= startIndex - count; i--) {
1042 for (int j = 0; j < strlen (values); j++) {
1043 if (this.c_str[i] == values[j])
1051 public string PadLeft (int totalWidth)
1053 return PadLeft (totalWidth, ' ');
1056 public string PadLeft (int totalWidth, char padChar)
1062 throw new ArgumentException ();
1064 str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
1065 for (i = 0; i < totalWidth - this.length; i++)
1068 for (j = 0; j < this.length; i++, j++)
1069 str[i] = this.c_str[j];
1073 return new String (str);
1076 public string PadRight (int totalWidth)
1078 return PadRight (totalWidth, ' ');
1081 public string PadRight (int totalWidth, char padChar)
1087 throw new ArgumentException ();
1089 str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
1090 for (i = 0; i < this.length; i++)
1091 str[i] = this.c_str[i];
1093 for ( ; i < str.Length; i++)
1098 return new String (str);
1101 public string Remove (int startIndex, int count)
1106 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1107 throw new ArgumentOutOfRangeException ();
1109 len = this.length - count;
1111 return String.Empty;
1113 str = new char [len + 1];
1114 for (i = 0; i < startIndex; i++)
1115 str[i] = this.c_str[i];
1116 for (j = i + count; j < this.length; j++)
1117 str[i++] = this.c_str[j];
1120 return new String (str);
1123 public string Replace (char oldChar, char newChar)
1128 str = new char [this.length + 1];
1129 for (i = 0; i < this.length; i++) {
1130 if (this.c_str[i] == oldChar)
1133 str[i] = this.c_str[i];
1137 return new String (str);
1140 public string Replace (string oldValue, string newValue)
1142 // LAMESPEC: msdn doesn't specify what to do if either args is null
1143 int index, len, i, j;
1146 if (oldValue == null || newValue == null)
1147 throw new ArgumentNullException ();
1149 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1150 index = IndexOf (oldValue, 0);
1152 // This is the easy one ;-)
1153 return Substring (0, this.length);
1156 len = this.length - oldValue.Length + newValue.Length;
1158 return String.Empty;
1160 str = new char [len + 1];
1161 for (i = 0; i < index; i++)
1162 str[i] = this.c_str[i];
1163 for (j = 0; j < newValue.Length; j++)
1164 str[i++] = newValue[j];
1165 for (j = index + oldValue.Length; j < this.length; j++)
1166 str[i++] = this.c_str[j];
1169 return new String (str);
1172 private int splitme (char[] separators, int startIndex)
1174 /* this is basically a customized IndexOfAny() for the Split() methods */
1175 for (int i = startIndex; i < this.length; i++) {
1176 if (separators != null) {
1177 foreach (char sep in separators) {
1178 if (this.c_str[i] == sep)
1179 return i - startIndex;
1181 } else if (is_lwsp (this.c_str[i])) {
1182 return i - startIndex;
1189 public string[] Split (params char[] separator)
1193 * @separator: delimiting chars or null to split on whtspc
1195 * Returns: 1. An array consisting of a single
1196 * element (@this) if none of the delimiting
1197 * chars appear in @this. 2. An array of
1198 * substrings which are delimited by one of
1199 * the separator chars. 3. An array of
1200 * substrings separated by whitespace if
1201 * @separator is null. The Empty string should
1202 * be returned wherever 2 delimiting chars are
1205 // FIXME: would using a Queue be better?
1210 list = new ArrayList ();
1211 for (index = 0, len = 0; index < this.length; index += len + 1) {
1212 len = splitme (separator, index);
1213 len = len > -1 ? len : this.length - index;
1215 list.Add (String.Empty);
1220 str = new char [len + 1];
1221 for (i = 0; i < len; i++)
1222 str[i] = this.c_str[index + i];
1225 list.Add (new String (str));
1229 strings = new string [list.Count];
1230 if (list.Count == 1) {
1231 /* special case for an array holding @this */
1234 for (index = 0; index < list.Count; index++)
1235 strings[index] = (string) list[index];
1241 public string[] Split (char[] separator, int maxCount)
1243 // FIXME: what to do if maxCount <= 0?
1244 // FIXME: would using Queue be better than ArrayList?
1247 int index, len, used;
1250 list = new ArrayList ();
1251 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1252 len = splitme (separator, index);
1253 len = len > -1 ? len : this.length - index;
1255 list.Add (String.Empty);
1260 str = new char [len + 1];
1261 for (i = 0; i < len; i++)
1262 str[i] = this.c_str[index + i];
1265 list.Add (new String (str));
1270 /* fit the remaining chunk of the @this into it's own element */
1271 if (index != this.length) {
1275 str = new char [this.length - index + 1];
1276 for (i = index; i < this.length; i++)
1277 str[i - index] = this.c_str[i];
1278 str[i - index] = '\0';
1280 list.Add (new String (str));
1283 strings = new string [list.Count];
1284 if (list.Count == 1) {
1285 /* special case for an array holding @this */
1288 for (index = 0; index < list.Count; index++)
1289 strings[index] = (string) list[index];
1295 public bool StartsWith (string value)
1297 bool startswith = true;
1301 throw new ArgumentNullException ();
1303 if (value.Length > this.length)
1306 for (i = 0; i < value.Length && startswith; i++)
1307 startswith = startswith && value[i] == this.c_str[i];
1312 public string Substring (int startIndex)
1317 if (startIndex < 0 || startIndex > this.length)
1318 throw new ArgumentOutOfRangeException ();
1320 len = this.length - startIndex;
1322 return String.Empty;
1324 str = new char [len + 1];
1325 for (i = startIndex; i < this.length; i++)
1326 str[i - startIndex] = this.c_str[i];
1328 return new String (str);
1331 public string Substring (int startIndex, int length)
1336 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1337 throw new ArgumentOutOfRangeException ();
1340 return String.Empty;
1342 str = new char [length + 1];
1343 for (i = startIndex; i < startIndex + length; i++)
1344 str[i - startIndex] = this.c_str[i];
1347 return new String (str);
1350 public bool ToBoolean (IFormatProvider provider)
1352 // FIXME: implement me
1356 public byte ToByte (IFormatProvider provider)
1358 // FIXME: implement me
1362 public char ToChar (IFormatProvider provider)
1364 // FIXME: implement me
1368 public char[] ToCharArray ()
1370 return ToCharArray (0, this.length);
1373 public char[] ToCharArray (int startIndex, int length)
1378 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1379 throw new ArgumentOutOfRangeException ();
1381 chars = new char [length + 1];
1382 for (i = startIndex; i < length; i++)
1383 chars[i - startIndex] = this.c_str[i];
1385 chars[length] = '\0';
1390 public DateTime ToDateTime (IFormatProvider provider)
1392 // FIXME: implement me
1394 return new DateTime (0);
1397 public decimal ToDecimal (IFormatProvider provider)
1399 // FIXME: implement me
1403 public double ToDouble (IFormatProvider provider)
1405 // FIXME: implement me
1409 public short ToInt16 (IFormatProvider provider)
1411 // FIXME: implement me
1415 public int ToInt32 (IFormatProvider provider)
1417 // FIXME: implement me
1421 public long ToInt64 (IFormatProvider provider)
1423 // FIXME: implement me
1427 public string ToLower ()
1432 str = new char [this.length + 1];
1433 for (i = 0; i < this.length; i++)
1434 str[i] = Char.ToLower (this.c_str[i]);
1437 return new String (str);
1440 public string ToLower (CultureInfo culture)
1442 // FIXME: implement me
1446 public sbyte ToSByte (IFormatProvider provider)
1448 // FIXME: implement me
1452 public float ToSingle (IFormatProvider provider)
1454 // FIXME: implement me
1458 public override string ToString ()
1460 return Substring (0, this.length);
1463 public string ToString (IFormatProvider format)
1465 // FIXME: implement me
1469 public object ToType (Type conversionType, IFormatProvider provider)
1471 // FIXME: implement me
1475 public ushort ToUInt16 (IFormatProvider provider)
1477 // FIXME: implement me
1481 public uint ToUInt32 (IFormatProvider provider)
1483 // FIXME: implement me
1487 public ulong ToUInt64 (IFormatProvider provider)
1489 // FIXME: implement me
1493 public string ToUpper ()
1498 str = new char [this.length + 1];
1499 for (i = 0; i < this.length; i++)
1500 str[i] = Char.ToUpper (this.c_str[i]);
1503 return new String (str);
1506 public string ToUpper (CultureInfo culture)
1508 // FIXME: implement me
1512 public string Trim ()
1517 public string Trim (params char[] trimChars)
1523 for (begin = 0; matches && begin < this.length; begin++) {
1524 if (trimChars != null) {
1526 foreach (char c in trimChars) {
1527 matches = this.c_str[begin] == c;
1532 matches = is_lwsp (this.c_str[begin]);
1537 for (end = this.length-1; end > begin; end--) {
1538 if (trimChars != null) {
1540 foreach (char c in trimChars) {
1541 matches = this.c_str[end] == c;
1546 matches = is_lwsp (this.c_str[end]);
1551 return String.Empty;
1553 return Substring (begin, end - begin);
1556 public string TrimEnd (params char[] trimChars)
1558 bool matches = true;
1561 for (end = this.length; end > 0; end--) {
1562 if (trimChars != null) {
1564 foreach (char c in trimChars) {
1565 matches = this.c_str[end] == c;
1570 matches = is_lwsp (this.c_str[end]);
1575 return String.Empty;
1577 return Substring (0, end);
1580 public string TrimStart (params char[] trimChars)
1582 bool matches = true;
1585 for (begin = 0; matches && begin < this.length; begin++) {
1586 if (trimChars != null) {
1588 foreach (char c in trimChars) {
1589 matches = this.c_str[begin] == c;
1594 matches = is_lwsp (this.c_str[begin]);
1598 if (begin == this.length)
1599 return String.Empty;
1601 return Substring (begin, this.length - begin);
1605 public static bool operator ==(string a, string b)
1607 if (a.length != b.length)
1611 for (int i = 0; i < l; i++)
1612 if (a.c_str[i] != b.c_str[i])
1618 public static bool operator !=(string a, string b)