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)]
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)
190 private static char tolowerordinal (char c)
192 // FIXME: implement me
196 private static bool is_lwsp (char c)
198 /* this comes from the msdn docs for String.Trim() */
199 if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
200 (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
206 private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
208 /* (hopefully) Unicode-safe Boyer-Moore implementation */
209 int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
210 int h, n, he, ne, hc, nc, i;
212 if (haystack == null || needle == null)
213 throw new ArgumentNullException ();
215 /* if the search buffer is shorter than the pattern buffer, we can't match */
216 if (count < needle.Length)
219 /* return an instant match if the pattern is 0-length */
220 if (needle.Length == 0)
223 /* set a pointer at the end of each string */
224 ne = needle.Length - 1; /* position of char before '\0' */
225 he = startIndex + count; /* position of last valid char */
227 /* init the skip table with the pattern length */
228 for (i = 0; i < 65536; i++)
229 skiptable[i] = needle.Length;
231 /* set the skip value for the chars that *do* appear in the
232 * pattern buffer (needle) to the distance from the index to
233 * the end of the pattern buffer. */
234 for (nc = 0; nc < ne; nc++)
235 skiptable[(int) needle[nc]] = ne - nc;
238 while (count >= needle.Length) {
239 hc = h + needle.Length - 1; /* set the haystack compare pointer */
240 nc = ne; /* set the needle compare pointer */
242 /* work our way backwards until they don't match */
243 for (i = 0; nc > 0; nc--, hc--, i++)
244 if (needle[nc] != haystack[hc])
247 if (needle[nc] != haystack[hc]) {
248 n = skiptable[(int) haystack[hc]] - i;
259 public object Clone ()
261 // FIXME: implement me
265 public static int Compare (string strA, string strB)
274 } else if (strB == null)
277 min = strA.Length < strB.Length ? strA.Length : strB.Length;
279 for (i = 0; strA[i] == strB[i] && i < min; i++);
281 return ((int) (strA[i] - strB[i]));
284 public static int Compare (string strA, string strB, bool ignoreCase)
289 return Compare (strA, strB);
292 * And here I thought Eazel developers were on crack...
293 * if a string is null it should pelt the programmer with
294 * ArgumentNullExceptions, damnit!
301 } else if (strB == null)
304 min = strA.Length < strB.Length ? strA.Length : strB.Length;
306 for (i = 0; i < min; i++) {
307 if (Char.ToLower (strA[i]) != Char.ToLower (strB[i]))
311 return ((int) (strA[i] - strB[i]));
314 public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
316 // FIXME: implement me
320 public static int Compare (string strA, int indexA, string strB, int indexB, int length)
324 if (length < 0 || indexA < 0 || indexB < 0)
325 throw new ArgumentOutOfRangeException ();
327 if (indexA > strA.Length || indexB > strB.Length)
328 throw new ArgumentOutOfRangeException ();
330 /* And again with the ("" > null) logic... lord have mercy! */
336 } else if (strB == null)
339 for (i = 0; i < length - 1; i++) {
340 if (strA[indexA + i] != strB[indexB + i])
344 return ((int) (strA[indexA + i] - strB[indexB + i]));
347 public static int Compare (string strA, int indexA, string strB, int indexB,
348 int length, bool ignoreCase)
353 return Compare (strA, indexA, strB, indexB, length);
355 if (length < 0 || indexA < 0 || indexB < 0)
356 throw new ArgumentOutOfRangeException ();
358 if (indexA > strA.Length || indexB > strB.Length)
359 throw new ArgumentOutOfRangeException ();
361 /* When will the hurting stop!?!? */
367 } else if (strB == null)
370 for (i = 0; i < length - 1; i++) {
371 if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
375 return ((int) (strA[indexA + i] - strB[indexB + i]));
378 public static int Compare (string strA, int indexA, string strB, int indexB,
379 int length, bool ignoreCase, CultureInfo culture)
382 throw new ArgumentNullException ();
384 if (length < 0 || indexA < 0 || indexB < 0)
385 throw new ArgumentOutOfRangeException ();
387 if (indexA > strA.Length || indexB > strB.Length)
388 throw new ArgumentOutOfRangeException ();
390 /* I can't take it anymore! */
396 } else if (strB == null)
399 // FIXME: implement me
403 public static int CompareOrdinal (string strA, string strB)
407 /* Please God, make it stop! */
413 } else if (strB == null)
416 for (i = 0; i < strA.Length && i < strB.Length; i++) {
419 cA = tolowerordinal (strA[i]);
420 cB = tolowerordinal (strB[i]);
426 return ((int) (strA[i] - strB[i]));
429 public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
434 if (length < 0 || indexA < 0 || indexB < 0)
435 throw new ArgumentOutOfRangeException ();
442 } else if (strB == null)
445 for (i = 0; i < length; i++) {
448 cA = tolowerordinal (strA[indexA + i]);
449 cB = tolowerordinal (strB[indexB + i]);
455 return ((int) (strA[indexA + i] - strB[indexB + i]));
458 public int CompareTo (object obj)
460 return Compare (this, obj == null ? null : obj.ToString ());
463 public int CompareTo (string str)
465 return Compare (this, str);
468 public static string Concat (object arg)
470 return arg != null ? arg.ToString () : String.Empty;
473 public static string Concat (params object[] args)
480 throw new ArgumentNullException ();
482 strings = new string [args.Length];
485 foreach (object arg in args) {
486 /* use Empty for each null argument */
488 strings[i] = String.Empty;
490 strings[i] = arg.ToString ();
491 len += strings[i].length;
498 String res = new String (len);
501 for (int j = 0; j < strings.Length; j++)
502 for (int k = 0; k < strings[j].length; k++)
503 str[i++] = strings[j].c_str[k];
508 public static string Concat (params string[] values)
514 throw new ArgumentNullException ();
517 foreach (string value in values)
518 len += value != null ? value.Length : 0;
523 String res = new String (len);
526 foreach (string value in values) {
530 for (int j = 0; j < value.length; j++)
531 str[i++] = value.c_str[j];
537 public static string Concat (object arg0, object arg1)
539 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
540 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
542 return Concat (str0, str1);
545 public static string Concat (string str0, string str1)
555 len = str0.length + str1.length;
559 String res = new String (len);
562 for (i = 0; i < str0.length; i++)
563 concat[i] = str0.c_str[i];
564 for (j = 0 ; j < str1.length; j++)
565 concat[i + j] = str1.c_str[j];
570 public static string Concat (object arg0, object arg1, object arg2)
572 string str0 = arg0 != null ? arg0.ToString () : String.Empty;
573 string str1 = arg1 != null ? arg1.ToString () : String.Empty;
574 string str2 = arg2 != null ? arg2.ToString () : String.Empty;
576 return Concat (str0, str1, str2);
579 public static string Concat (string str0, string str1, string str2)
591 len = str0.length + str1.length + str2.length;
595 String res = new String (len);
598 for (i = 0; i < str0.length; i++)
599 concat[i] = str0.c_str[i];
600 for (j = 0; j < str1.length; j++)
601 concat[i + j] = str1.c_str[j];
602 for (k = 0; k < str2.length; k++)
603 concat[i + j + k] = str2.c_str[k];
608 public static string Concat (string str0, string str1, string str2, string str3)
622 len = str0.length + str1.length + str2.length + str3.length;
625 String res = new String (len);
628 for (i = 0; i < str0.length; i++)
629 concat[i] = str0.c_str[i];
630 for (j = 0; j < str1.length; j++)
631 concat[i + j] = str1.c_str[j];
632 for (k = 0; k < str2.length; k++)
633 concat[i + j + k] = str2.c_str[k];
634 for (l = 0; l < str3.length; l++)
635 concat[i + j + k + l] = str3.c_str[l];
640 public static string Copy (string str)
642 // FIXME: how do I *copy* a string if I can only have 1 of each?
644 throw new ArgumentNullException ();
649 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
651 // LAMESPEC: should I null-terminate?
654 if (destination == null)
655 throw new ArgumentNullException ();
657 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
658 throw new ArgumentOutOfRangeException ();
660 if (sourceIndex + count > this.length)
661 throw new ArgumentOutOfRangeException ();
663 if (destinationIndex + count > destination.Length)
664 throw new ArgumentOutOfRangeException ();
666 for (i = 0; i < count; i++)
667 destination[destinationIndex + i] = this.c_str[sourceIndex + i];
670 public bool EndsWith (string value)
672 bool endswith = true;
676 throw new ArgumentNullException ();
678 start = this.length - value.length;
682 for (i = start; i < this.length && endswith; i++)
683 endswith = this.c_str[i] == value.c_str[i - start];
688 public override bool Equals (object obj)
690 if (!(obj is String))
693 return this == (String) obj;
696 public bool Equals (string value)
698 return this == value;
701 public static bool Equals (string a, string b)
706 public static string Format (string format, object arg0)
708 // FIXME: implement me
712 public static string Format (string format, params object[] args)
714 // FIXME: implement me
718 public static string Format (IFormatProvider provider, string format, params object[] args)
720 // FIXME: implement me
724 public static string Format (string format, object arg0, object arg1)
726 // FIXME: implement me
730 public static string Format (string format, object arg0, object arg1, object arg2)
732 // FIXME: implement me
736 //public CharEnumerator GetEnumerator ()
737 public IEnumerator GetEnumerator ()
739 // FIXME: implement me
743 public override int GetHashCode ()
747 for (i = 0; i < length; ++i)
748 h = (h << 5) - h + c_str [i];
752 public TypeCode GetTypeCode ()
754 return TypeCode.String;
757 public int IndexOf (char value)
759 return IndexOf (value, 0, this.length);
762 public int IndexOf (string value)
764 return IndexOf (value, 0, this.length);
767 public int IndexOf (char value, int startIndex)
769 return IndexOf (value, startIndex, this.length - startIndex);
772 public int IndexOf (string value, int startIndex)
774 return IndexOf (value, startIndex, this.length - startIndex);
777 public int IndexOf (char value, int startIndex, int count)
781 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
782 throw new ArgumentOutOfRangeException ();
784 for (i = startIndex; i - startIndex < count; i++)
785 if (this.c_str[i] == value)
791 public int IndexOf (string value, int startIndex, int count)
794 throw new ArgumentNullException ();
796 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
797 throw new ArgumentOutOfRangeException ();
799 return BoyerMoore (this.c_str, value, startIndex, count);
802 for (i = startIndex; i - startIndex + value.Length <= count; ) {
803 if (this.c_str[i] == value[0]) {
807 for (j = 1; equal && j < value.Length; j++) {
808 equal = this.c_str[i + j] == value[j];
809 if (this.c_str[i + j] == value[0] && nexti == 0)
828 public int IndexOfAny (char[] values)
830 return IndexOfAny (values, 0, this.length);
833 public int IndexOfAny (char[] values, int startIndex)
835 return IndexOfAny (values, startIndex, this.length - startIndex);
838 public int IndexOfAny (char[] values, int startIndex, int count)
841 throw new ArgumentNullException ();
843 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
844 throw new ArgumentOutOfRangeException ();
846 for (int i = startIndex; i < startIndex + count; i++) {
847 for (int j = 0; j < strlen (values); j++) {
848 if (this.c_str[i] == values[j])
856 public string Insert (int startIndex, string value)
862 throw new ArgumentNullException ();
864 if (startIndex < 0 || startIndex > this.length)
865 throw new ArgumentOutOfRangeException ();
867 String res = new String (value.length + this.length);
870 for (i = 0; i < startIndex; i++)
871 str[i] = this.c_str[i];
872 for (j = 0; j < value.length; j++)
873 str[i + j] = value.c_str[j];
874 for ( ; i < this.length; i++)
875 str[i + j] = this.c_str[i];
880 [MethodImplAttribute(MethodImplOptions.InternalCall)]
881 public extern static string Intern (string str);
883 [MethodImplAttribute(MethodImplOptions.InternalCall)]
884 public extern static string IsInterned (string str);
886 public static string Join (string separator, string[] value)
888 return Join (separator, value, 0, value.Length);
891 public static string Join (string separator, string[] value, int startIndex, int count)
893 // LAMESPEC: msdn doesn't specify what happens when separator is null
897 if (separator == null || value == null)
898 throw new ArgumentNullException ();
900 if (startIndex + count > value.Length)
901 throw new ArgumentOutOfRangeException ();
904 for (i = startIndex, used = 0; used < count; i++, used++) {
906 len += separator.length;
908 len += value[i].length;
911 // We have no elements to join?
915 String res = new String (len);
918 for (i = 0; i < value[startIndex].length; i++)
919 str[i] = value[startIndex][i];
922 for (j = startIndex + 1; used < count; j++, used++) {
925 for (k = 0; k < separator.length; k++)
926 str[i++] = separator.c_str[k];
927 for (k = 0; k < value[j].length; k++)
928 str[i++] = value[j].c_str[k];
934 public int LastIndexOf (char value)
940 for (; i >= 0; i--) {
941 if (this.c_str[i] == value)
948 public int LastIndexOf (string value)
950 return LastIndexOf (value, this.length, this.length);
953 public int LastIndexOf (char value, int startIndex)
955 if (startIndex < 0 || startIndex > this.length)
956 throw new ArgumentOutOfRangeException ();
958 for (int i = startIndex; i >= 0; i--) {
959 if (this.c_str[i] == value)
966 public int LastIndexOf (string value, int startIndex)
968 return LastIndexOf (value, startIndex, this.length);
971 public int LastIndexOf (char value, int startIndex, int count)
973 if (startIndex < 0 || count < 0)
974 throw new ArgumentOutOfRangeException ();
976 if (startIndex > this.length || startIndex - count < 0)
977 throw new ArgumentOutOfRangeException ();
979 for (int i = startIndex; i >= startIndex - count; i--) {
980 if (this.c_str[i] == value)
987 public int LastIndexOf (string value, int startIndex, int count)
989 // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
990 // but maybe it's the end-position in MS's implementation?
991 // msdn is unclear on this point. I think this is correct though.
995 throw new ArgumentNullException ();
997 if (startIndex < 0 || startIndex > this.length)
998 throw new ArgumentOutOfRangeException ();
1000 if (count < 0 || startIndex - count < 0)
1001 throw new ArgumentOutOfRangeException ();
1003 if (value == String.Empty)
1006 if (startIndex + value.length > this.length) {
1007 /* just a little optimization */
1010 start = this.length - value.length;
1011 count -= startIndex - start;
1015 // FIXME: use a reversed-unicode-safe-Boyer-Moore?
1016 len = value.length - 1;
1017 for (i = startIndex; i >= startIndex - count; i--) {
1018 if (this.c_str[i + len] == value.c_str[len]) {
1022 for (j = len - 1; equal && j >= 0; j--)
1023 equal = this.c_str[i + j] == value.c_str[j];
1033 public int LastIndexOfAny (char[] values)
1035 return LastIndexOfAny (values, this.length, this.length);
1038 public int LastIndexOfAny (char[] values, int startIndex)
1040 return LastIndexOfAny (values, startIndex, startIndex);
1043 public int LastIndexOfAny (char[] values, int startIndex, int count)
1048 throw new ArgumentNullException ();
1050 if (startIndex < 0 || count < 0 || startIndex - count < 0)
1051 throw new ArgumentOutOfRangeException ();
1053 for (i = startIndex; i >= startIndex - count; i--) {
1054 for (int j = 0; j < strlen (values); j++) {
1055 if (this.c_str[i] == values[j])
1063 public string PadLeft (int totalWidth)
1065 return PadLeft (totalWidth, ' ');
1068 public string PadLeft (int totalWidth, char padChar)
1074 throw new ArgumentException ();
1076 str = new char [totalWidth > this.length ? totalWidth : this.length];
1077 for (i = 0; i < totalWidth - this.length; i++)
1080 for (j = 0; j < this.length; i++, j++)
1081 str[i] = this.c_str[j];
1083 return new String (str);
1086 public string PadRight (int totalWidth)
1088 return PadRight (totalWidth, ' ');
1091 public string PadRight (int totalWidth, char padChar)
1097 throw new ArgumentException ();
1099 str = new char [totalWidth > this.length ? totalWidth : this.length];
1100 for (i = 0; i < this.length; i++)
1101 str[i] = this.c_str[i];
1103 for ( ; i < str.Length; i++)
1106 return new String (str);
1109 public string Remove (int startIndex, int count)
1114 if (startIndex < 0 || count < 0 || startIndex + count > this.length)
1115 throw new ArgumentOutOfRangeException ();
1117 len = this.length - count;
1119 return String.Empty;
1121 String res = new String (len);
1123 for (i = 0; i < startIndex; i++)
1124 str[i] = this.c_str[i];
1125 for (j = i + count; j < this.length; j++)
1126 str[i++] = this.c_str[j];
1131 public string Replace (char oldChar, char newChar)
1136 String res = new String (length);
1138 for (i = 0; i < this.length; i++) {
1139 if (this.c_str[i] == oldChar)
1142 str[i] = this.c_str[i];
1148 public string Replace (string oldValue, string newValue)
1150 // LAMESPEC: msdn doesn't specify what to do if either args is null
1151 int index, len, i, j;
1154 if (oldValue == null || newValue == null)
1155 throw new ArgumentNullException ();
1157 // Use IndexOf in case I later rewrite it to use Boyer-Moore
1158 index = IndexOf (oldValue, 0);
1160 // This is the easy one ;-)
1161 return Substring (0, this.length);
1164 len = this.length - oldValue.length + newValue.length;
1166 return String.Empty;
1168 String res = new String (len);
1170 for (i = 0; i < index; i++)
1171 str[i] = this.c_str[i];
1172 for (j = 0; j < newValue.length; j++)
1173 str[i++] = newValue[j];
1174 for (j = index + oldValue.length; j < this.length; j++)
1175 str[i++] = this.c_str[j];
1180 private int splitme (char[] separators, int startIndex)
1182 /* this is basically a customized IndexOfAny() for the Split() methods */
1183 for (int i = startIndex; i < this.length; i++) {
1184 if (separators != null) {
1185 foreach (char sep in separators) {
1186 if (this.c_str[i] == sep)
1187 return i - startIndex;
1189 } else if (is_lwsp (this.c_str[i])) {
1190 return i - startIndex;
1197 public string[] Split (params char[] separator)
1201 * @separator: delimiting chars or null to split on whtspc
1203 * Returns: 1. An array consisting of a single
1204 * element (@this) if none of the delimiting
1205 * chars appear in @this. 2. An array of
1206 * substrings which are delimited by one of
1207 * the separator chars. 3. An array of
1208 * substrings separated by whitespace if
1209 * @separator is null. The Empty string should
1210 * be returned wherever 2 delimiting chars are
1213 // FIXME: would using a Queue be better?
1218 list = new ArrayList ();
1219 for (index = 0, len = 0; index < this.length; index += len + 1) {
1220 len = splitme (separator, index);
1221 len = len > -1 ? len : this.length - index;
1223 list.Add (String.Empty);
1228 str = new char [len];
1229 for (i = 0; i < len; i++)
1230 str[i] = this.c_str[index + i];
1232 list.Add (new String (str));
1236 strings = new string [list.Count];
1237 if (list.Count == 1) {
1238 /* special case for an array holding @this */
1241 for (index = 0; index < list.Count; index++)
1242 strings[index] = (string) list[index];
1248 public string[] Split (char[] separator, int maxCount)
1250 // FIXME: what to do if maxCount <= 0?
1251 // FIXME: would using Queue be better than ArrayList?
1254 int index, len, used;
1257 list = new ArrayList ();
1258 for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
1259 len = splitme (separator, index);
1260 len = len > -1 ? len : this.length - index;
1262 list.Add (String.Empty);
1267 str = new char [len];
1268 for (i = 0; i < len; i++)
1269 str[i] = this.c_str[index + i];
1271 list.Add (new String (str));
1276 /* fit the remaining chunk of the @this into it's own element */
1277 if (index != this.length) {
1281 str = new char [this.length - index];
1282 for (i = index; i < this.length; i++)
1283 str[i - index] = this.c_str[i];
1285 list.Add (new String (str));
1288 strings = new string [list.Count];
1289 if (list.Count == 1) {
1290 /* special case for an array holding @this */
1293 for (index = 0; index < list.Count; index++)
1294 strings[index] = (string) list[index];
1300 public bool StartsWith (string value)
1302 bool startswith = true;
1306 throw new ArgumentNullException ();
1308 if (value.length > this.length)
1311 for (i = 0; i < value.length && startswith; i++)
1312 startswith = startswith && value.c_str[i] == this.c_str[i];
1317 public string Substring (int startIndex)
1322 if (startIndex < 0 || startIndex > this.length)
1323 throw new ArgumentOutOfRangeException ();
1325 len = this.length - startIndex;
1327 return String.Empty;
1328 String res = new String (len);
1330 for (i = startIndex; i < this.length; i++)
1331 str[i - startIndex] = this.c_str[i];
1336 public string Substring (int startIndex, int length)
1341 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1342 throw new ArgumentOutOfRangeException ();
1345 return String.Empty;
1347 String res = new String (length);
1349 for (i = startIndex; i < startIndex + length; i++)
1350 str[i - startIndex] = this.c_str[i];
1355 public bool ToBoolean (IFormatProvider provider)
1357 // FIXME: implement me
1358 throw new NotImplementedException ();
1361 public byte ToByte (IFormatProvider provider)
1363 // FIXME: implement me
1364 throw new NotImplementedException ();
1367 public char ToChar (IFormatProvider provider)
1369 // FIXME: implement me
1370 throw new NotImplementedException ();
1373 public char[] ToCharArray ()
1375 return ToCharArray (0, this.length);
1378 public char[] ToCharArray (int startIndex, int length)
1383 if (startIndex < 0 || length < 0 || startIndex + length > this.length)
1384 throw new ArgumentOutOfRangeException ();
1386 chars = new char [length];
1387 for (i = startIndex; i < length; i++)
1388 chars[i - startIndex] = this.c_str[i];
1393 public DateTime ToDateTime (IFormatProvider provider)
1395 // FIXME: implement me
1396 // return new DateTime (0);
1397 throw new NotImplementedException ();
1400 public decimal ToDecimal (IFormatProvider provider)
1402 // FIXME: implement me
1403 throw new NotImplementedException ();
1406 public double ToDouble (IFormatProvider provider)
1408 // FIXME: implement me
1409 throw new NotImplementedException ();
1412 public short ToInt16 (IFormatProvider provider)
1414 // FIXME: implement me
1415 throw new NotImplementedException ();
1418 public int ToInt32 (IFormatProvider provider)
1420 // FIXME: implement me
1421 throw new NotImplementedException ();
1424 public long ToInt64 (IFormatProvider provider)
1426 // FIXME: implement me
1427 throw new NotImplementedException ();
1430 public string ToLower ()
1435 String res = new String (length);
1437 for (i = 0; i < this.length; i++)
1438 str[i] = Char.ToLower (this.c_str[i]);
1443 public string ToLower (CultureInfo culture)
1445 // FIXME: implement me
1446 throw new NotImplementedException ();
1450 [CLSCompliant(false)]
1451 public sbyte ToSByte (IFormatProvider provider)
1453 // FIXME: implement me
1454 throw new NotImplementedException ();
1457 public float ToSingle (IFormatProvider provider)
1459 // FIXME: implement me
1460 throw new NotImplementedException ();
1463 public override string ToString ()
1468 public string ToString (IFormatProvider format)
1470 // FIXME: implement me
1471 throw new NotImplementedException ();
1474 public object ToType (Type conversionType, IFormatProvider provider)
1476 // FIXME: implement me
1477 throw new NotImplementedException ();
1480 [CLSCompliant(false)]
1481 public ushort ToUInt16 (IFormatProvider provider)
1483 // FIXME: implement me
1484 throw new NotImplementedException ();
1487 [CLSCompliant(false)]
1488 public uint ToUInt32 (IFormatProvider provider)
1490 // FIXME: implement me
1491 throw new NotImplementedException ();
1494 [CLSCompliant(false)]
1495 public ulong ToUInt64 (IFormatProvider provider)
1497 // FIXME: implement me
1498 throw new NotImplementedException ();
1501 public string ToUpper ()
1506 String res = new String (length);
1508 for (i = 0; i < this.length; i++)
1509 str[i] = Char.ToUpper (this.c_str[i]);
1514 public string ToUpper (CultureInfo culture)
1516 // FIXME: implement me
1517 throw new NotImplementedException ();
1520 public string Trim ()
1525 public string Trim (params char[] trimChars)
1531 for (begin = 0; matches && begin < this.length; begin++) {
1532 if (trimChars != null) {
1534 foreach (char c in trimChars) {
1535 matches = this.c_str[begin] == c;
1540 matches = is_lwsp (this.c_str[begin]);
1545 for (end = this.length-1; end > begin; end--) {
1546 if (trimChars != null) {
1548 foreach (char c in trimChars) {
1549 matches = this.c_str[end] == c;
1554 matches = is_lwsp (this.c_str[end]);
1559 return String.Empty;
1561 return Substring (begin, end - begin);
1564 public string TrimEnd (params char[] trimChars)
1566 bool matches = true;
1569 for (end = this.length; end > 0; end--) {
1570 if (trimChars != null) {
1572 foreach (char c in trimChars) {
1573 matches = this.c_str[end] == c;
1578 matches = is_lwsp (this.c_str[end]);
1583 return String.Empty;
1585 return Substring (0, end);
1588 public string TrimStart (params char[] trimChars)
1590 bool matches = true;
1593 for (begin = 0; matches && begin < this.length; begin++) {
1594 if (trimChars != null) {
1596 foreach (char c in trimChars) {
1597 matches = this.c_str[begin] == c;
1602 matches = is_lwsp (this.c_str[begin]);
1606 if (begin == this.length)
1607 return String.Empty;
1609 return Substring (begin, this.length - begin);
1613 public static bool operator ==(string a, string b)
1615 if (a.length != b.length)
1619 for (int i = 0; i < l; i++)
1620 if (a.c_str[i] != b.c_str[i])
1626 public static bool operator !=(string a, string b)