5 // Patrik Torstensson (patrik.torstensson@labs2.com)
6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
9 // (C) 2001 Ximian, Inc. http://www.ximian.com
14 using System.Collections;
15 using System.Globalization;
16 using System.Runtime.CompilerServices;
19 public sealed class String : IConvertible, IComparable, ICloneable, IEnumerable {
22 public static readonly String Empty = "";
24 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
25 unsafe public extern String(char *value);
27 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
28 unsafe public extern String(char *value, int sindex, int length);
30 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
31 unsafe public extern String(sbyte *value);
33 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
34 unsafe public extern String(sbyte *value, int sindex, int length);
36 [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
37 unsafe public extern String(sbyte *value, int sindex, int length, Encoding enc);
39 [MethodImplAttribute(MethodImplOptions.InternalCall)]
40 public extern String(char [] val, int sindex, int length);
42 [MethodImplAttribute(MethodImplOptions.InternalCall)]
43 public extern String(char [] val);
45 [MethodImplAttribute(MethodImplOptions.InternalCall)]
46 public extern String(char c, int count);
48 [MethodImplAttribute(MethodImplOptions.InternalCall)]
49 public extern override int GetHashCode();
51 public static bool Equals(String str1, String str2) {
52 if ((Object) str1 == (Object) str2)
55 if (null == (Object) str1 || null == (Object) str2)
57 if (str1.length != str2.length)
60 return Compare(str1, 0, str2, 0, str1.length, false) == 0;
63 public static bool operator == (String str1, String str2) {
64 return Equals(str1, str2);
67 public static bool operator != (String str1, String str2) {
68 return !Equals(str1, str2);
71 public override bool Equals(Object obj) {
78 return Compare (this, (String)obj, false) == 0;
81 public bool Equals(String value) {
85 if (length != value.length)
87 return Compare (this, 0, value, 0, length, false) == 0;
90 [IndexerName("Chars")]
91 public extern char this[int index] {
92 [MethodImplAttribute(MethodImplOptions.InternalCall)]
96 public Object Clone() {
100 public TypeCode GetTypeCode () {
101 return TypeCode.String;
104 public void CopyTo(int sindex, char[] dest, int dindex, int count) {
105 // LAMESPEC: should I null-terminate?
108 throw new ArgumentNullException();
110 if (sindex < 0 || dindex < 0 || count < 0)
111 throw new ArgumentOutOfRangeException ();
113 if (sindex + count > Length)
114 throw new ArgumentOutOfRangeException ();
116 if (dindex + count > dest.Length)
117 throw new ArgumentOutOfRangeException ();
119 InternalCopyTo(sindex, dest, dindex, count);
122 public char[] ToCharArray() {
123 return ToCharArray(0, length);
126 public char[] ToCharArray(int sindex, int length) {
127 if (sindex < 0 || length < 0 || sindex + length > this.length)
128 throw new ArgumentOutOfRangeException ();
130 char [] tmp = new char[length];
132 InternalCopyTo(sindex, tmp, 0, length);
137 public String [] Split(params char [] separator) {
138 return Split(separator, Int32.MaxValue);
141 public String[] Split(char[] separator, int count) {
142 if (null == separator) {
143 separator = WhiteChars;
147 throw new ArgumentOutOfRangeException ();
150 return new String[1] { ToString() };
152 return InternalSplit(separator, count);
155 public String Substring (int sindex) {
156 if (sindex < 0 || sindex > this.length) {
157 throw new ArgumentOutOfRangeException();
160 string tmp = InternalAllocateStr(this.length - sindex);
161 InternalStrcpy(tmp, 0, this, sindex, length - sindex);
166 public String Substring (int sindex, int length) {
167 if (length < 0 || sindex < 0 || sindex + length > this.length) {
168 throw new ArgumentOutOfRangeException();
174 string tmp = InternalAllocateStr(length);
175 InternalStrcpy(tmp, 0, this, sindex, length);
180 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD, (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004, (char) 0x2005,
181 (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B, (char) 0x3000, (char) 0xFEFF };
183 public String Trim(params char[] chars) {
184 if (null == chars || chars.Length == 0)
187 return InternalTrim(chars, 0);
190 public String TrimStart(params char[] chars) {
191 if (null == chars || chars.Length == 0)
194 return InternalTrim(chars, 1);
197 public String TrimEnd(params char[] chars) {
198 if (null == chars || chars.Length == 0)
201 return InternalTrim(chars, 2);
204 public static int Compare(String s1, String s2) {
205 return Compare(s1, s2, false);
208 public static int Compare(String s1, String s2, bool inCase) {
214 } else if (null == s2)
217 return InternalCompare(s1, 0, s2, 0, Math.Max(s1.length, s2.length), inCase);
221 public static int Compare(String s1, String s2, bool inCase, CultureInfo culture) {
222 return Compare(s1, s2, inCase);
225 public static int Compare(String s1, int i1, String s2, int i2, int length) {
226 return Compare(s1, i1, s2, i2, length, false);
229 public static int Compare(String s1, int i1, String s2, int i2, int length, bool inCase) {
235 } else if (null == s2)
238 if (length < 0 || i1 < 0 || i2 < 0)
239 throw new ArgumentOutOfRangeException ();
241 if (i1 > s1.length || i2 > s2.length)
242 throw new ArgumentOutOfRangeException ();
247 return InternalCompare(s1, i1, s2, i2, length, inCase);
251 public static int Compare(String s1, int i1, String s2, int i2, int length, bool inCase, CultureInfo culture) {
252 return Compare(s1, i1, s2, i2, length, inCase);
255 public int CompareTo(Object value) {
259 if (!(value is String))
260 throw new ArgumentException();
262 return String.Compare(this, (String) value, false);
265 public int CompareTo(String str) {
269 return Compare(this, str, false);
272 public static int CompareOrdinal(String s1, String s2) {
273 if (null == s1 || null == s2) {
274 if ((Object)s1 == (Object) s2) {
278 return (s1 == null) ? -1 : 1;
281 return InternalCompare(s1, 0, s2, 0, Math.Max(s1.length, s2.length), false);
284 public static int CompareOrdinal(String s1, int i1, String s2, int i2, int length) {
285 if (null == s1 || null == s2) {
286 if ((Object)s1 == (Object) s2) {
290 return (s1 == null) ? -1 : 1;
293 if (i1 < 0 || i2 < 0 || length < 0)
294 throw new ArgumentOutOfRangeException ();
296 if (i1 > s1.length || i2 > s2.length)
297 throw new ArgumentOutOfRangeException ();
299 return InternalCompare(s1, i1, s2, i2, length, false);
302 public bool EndsWith(String value) {
304 throw new ArgumentNullException();
306 if (value.length > this.length) {
310 return (0 == Compare(this, length - value.length, value, 0, value.length));
313 public int IndexOfAny(char [] arr) {
315 throw new ArgumentNullException();
317 return InternalIndexOfAny(arr, 0, this.length);
320 public int IndexOfAny(char [] arr, int sindex) {
322 throw new ArgumentNullException();
323 if (sindex < 0 || sindex >= this.length)
324 throw new ArgumentOutOfRangeException();
326 return InternalIndexOfAny(arr, sindex, this.length - sindex);
329 public int IndexOfAny(char [] arr, int sindex, int count) {
331 throw new ArgumentNullException();
332 if (sindex < 0 || count < 0 || sindex + count > this.length)
333 throw new ArgumentOutOfRangeException ();
335 return InternalIndexOfAny(arr, sindex, count);
338 public int IndexOf(char value) {
339 return InternalIndexOf(value, 0, this.length);
342 public int IndexOf(String value) {
343 return IndexOf(value, 0, this.length);
346 public int IndexOf(char value, int sindex) {
347 if (sindex < 0 || sindex >= this.length) {
348 throw new ArgumentOutOfRangeException();
351 return InternalIndexOf(value, sindex, this.length - sindex);
354 public int IndexOf(String value, int sindex) {
355 return IndexOf(value, sindex, this.length - sindex);
358 public int IndexOf(char value, int sindex, int count) {
359 if (sindex < 0 || count < 0 || sindex + count > this.length)
360 throw new ArgumentOutOfRangeException ();
362 if (sindex == 0 && this.length == 0)
365 return InternalIndexOf(value, sindex, count);
368 public int IndexOf(String value, int sindex, int count) {
370 throw new ArgumentNullException();
372 if (sindex < 0 || count < 0 || sindex + count > this.length)
373 throw new ArgumentOutOfRangeException ();
375 if (sindex == 0 && this.length == 0)
378 return InternalIndexOf(value, sindex, count);
381 public int LastIndexOfAny(char [] arr) {
383 throw new ArgumentNullException();
385 return InternalLastIndexOfAny(arr, this.length - 1, this.length);
388 public int LastIndexOfAny(char [] arr, int sindex) {
390 throw new ArgumentNullException();
392 if (sindex < 0 || sindex > this.length)
393 throw new ArgumentOutOfRangeException();
395 if (this.length == 0)
398 return InternalLastIndexOfAny(arr, sindex, sindex + 1);
401 public int LastIndexOfAny(char [] arr, int sindex, int count) {
403 throw new ArgumentNullException();
405 if (sindex < 0 || count < 0 || sindex > this.length || sindex - count < -1)
406 throw new ArgumentOutOfRangeException();
408 if (this.length == 0)
411 return InternalLastIndexOfAny(arr, sindex, count);
414 public int LastIndexOf(char value) {
415 return InternalLastIndexOf(value, this.length - 1, this.length);
418 public int LastIndexOf(String value) {
420 throw new ArgumentNullException();
422 return InternalLastIndexOf(value, this.length - 1, this.length);
425 public int LastIndexOf(char value, int sindex){
426 return LastIndexOf(value, sindex, sindex + 1);
429 public int LastIndexOf(String value, int sindex) {
430 return LastIndexOf(value, sindex, sindex + 1);
433 public int LastIndexOf(char value, int sindex, int count) {
434 if (count < 0 || sindex < 0)
435 throw new ArgumentOutOfRangeException ();
437 if (count > this.length || sindex > this.length)
438 throw new ArgumentOutOfRangeException ();
440 if (sindex == 0 && this.length == 0)
443 return InternalLastIndexOf(value, sindex, count);
446 public int LastIndexOf(String value, int sindex, int count) {
448 throw new ArgumentNullException();
450 if (sindex < 0 || sindex > this.length)
451 throw new ArgumentOutOfRangeException ();
453 if (count < 0 || count - sindex <= 0)
454 throw new ArgumentOutOfRangeException ();
456 if (sindex == 0 && this.length == 0)
459 return InternalLastIndexOf(value, sindex, count);
462 public String PadLeft(int width) {
463 return PadLeft(width, ' ');
466 public String PadLeft(int width, char chr) {
468 throw new ArgumentException();
470 if (width < this.length)
471 return String.Copy(this);
473 return InternalPad(width, chr, false);
476 public String PadRight(int width) {
477 return PadRight(width, ' ');
480 public String PadRight(int width, char chr) {
482 throw new ArgumentException();
484 if (width < this.length)
485 return String.Copy(this);
487 return InternalPad(width, chr, true);
490 public bool StartsWith(String value) {
492 throw new ArgumentNullException();
494 if (this.length < value.length)
497 return (0 == Compare(this, 0, value, 0 , value.length));
501 public String Replace (char oldChar, char newChar) {
502 return InternalReplace(oldChar, newChar);
505 public String Replace(String oldValue, String newValue) {
506 if (null == oldValue)
507 throw new ArgumentNullException();
509 return InternalReplace(oldValue, newValue);
512 public String Remove(int sindex, int count) {
513 if (sindex < 0 || count < 0 || sindex + count > this.length)
514 throw new ArgumentOutOfRangeException ();
516 return InternalRemove(sindex, count);
519 public String ToLower() {
520 return InternalToLower();
523 public String ToLower(CultureInfo culture) {
524 throw new NotImplementedException();
527 public String ToUpper() {
528 return InternalToUpper();
531 public String ToUpper(CultureInfo culture) {
532 throw new NotImplementedException();
535 public override String ToString() {
539 public String ToString(IFormatProvider provider) {
543 public String Trim() {
547 public static String Format(String format, Object arg0) {
548 return Format(null, format, new Object[] {arg0});
551 public static String Format(String format, Object arg0, Object arg1) {
552 return Format(null, format, new Object[] {arg0, arg1});
555 public static String Format(String format, Object arg0, Object arg1, Object arg2) {
556 return Format(null, format, new Object[] {arg0, arg1, arg2});
559 public static string Format (string format, params object[] args) {
560 return Format (null, format, args);
563 public static string Format (IFormatProvider provider, string format, params object[] args) {
564 if (format == null || args == null)
565 throw new ArgumentNullException ();
567 StringBuilder result = new StringBuilder ();
571 while (ptr < format.length) {
572 char c = format[ptr ++];
575 result.Append (format, start, ptr - start - 1);
577 // check for escaped open bracket
579 if (format[ptr] == '{') {
590 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
591 if (n >= args.Length)
592 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
596 object arg = args[n];
601 else if (arg is IFormattable)
602 str = ((IFormattable)arg).ToString (arg_format, provider);
604 str = arg.ToString ();
606 // pad formatted string and append to result
608 if (width > str.length) {
609 string pad = new String (' ', width - str.length);
625 else if (c == '}' && format[ptr] == '}') {
626 result.Append (format, start, ptr - start - 1);
631 if (start < format.length)
632 result.Append (format.Substring (start));
634 return result.ToString ();
637 public static String Copy (String str) {
639 throw new ArgumentNullException ();
641 int length = str.length;
643 String tmp = InternalAllocateStr(length);
644 InternalStrcpy(tmp, 0, str);
648 public static String Concat(Object obj) {
652 return obj.ToString();
655 public static String Concat(Object obj1, Object obj2) {
662 return Concat(obj1.ToString(), obj2.ToString());
665 public static String Concat(Object obj1, Object obj2, Object obj3) {
675 return Concat(obj1.ToString(), obj2.ToString(), obj3.ToString());
679 // I can not find the "__arglist" argument on the spec
682 [CLSCompliant(false)]
683 public static String Concat (Object arg0, Object arg1, Object arg2, Object arg3, __arglist)
685 throw new NotImplementedException();
688 public static String Concat(String s1, String s2) {
690 if (null == s2) { return String.Empty; }
694 if (null == s2) { return s1; }
696 String tmp = InternalAllocateStr(s1.length + s2.length);
698 InternalStrcpy(tmp, 0, s1);
699 InternalStrcpy(tmp, s1.length, s2);
704 public static String Concat(String s1, String s2, String s3) {
705 if (null == s1 && null == s2 && null == s3) {
709 if (null == s1) { s1 = String.Empty; }
710 if (null == s2) { s2 = String.Empty; }
711 if (null == s3) { s3 = String.Empty; }
713 String tmp = InternalAllocateStr(s1.length + s2.length + s3.length);
715 InternalStrcpy(tmp, 0, s1);
716 InternalStrcpy(tmp, s1.length, s2);
717 InternalStrcpy(tmp, s1.length + s2.length, s3);
722 public static String Concat(String s1, String s2, String s3, String s4) {
723 if (null == s1 && null == s2 && null == s3 && null == s4) {
727 if (null == s1) { s1 = String.Empty; }
728 if (null == s2) { s2 = String.Empty; }
729 if (null == s3) { s3 = String.Empty; }
730 if (null == s4) { s4 = String.Empty; }
732 String tmp = InternalAllocateStr(s1.length + s2.length + s3.length + s4.length);
734 InternalStrcpy(tmp, 0, s1);
735 InternalStrcpy(tmp, s1.length, s2);
736 InternalStrcpy(tmp, s1.length + s2.length, s3);
737 InternalStrcpy(tmp, s1.length + s2.length + s3.length, s4);
742 public static String Concat(params Object[] args) {
744 int len, i, currentpos;
747 throw new ArgumentNullException ();
749 strings = new string [args.Length];
752 foreach (object arg in args) {
753 /* use Empty for each null argument */
755 strings[i] = String.Empty;
757 strings[i] = arg.ToString ();
758 len += strings[i].length;
767 String tmp = InternalAllocateStr(len);
768 for (i = 0; i < strings.Length; i++) {
769 InternalStrcpy(tmp, currentpos, strings[i]);
770 currentpos += strings[i].length;
776 public static String Concat(params String[] values) {
777 int len, i, currentpos;
780 throw new ArgumentNullException ();
783 foreach (string value in values)
784 len += value != null ? value.length : 0;
791 String tmp = InternalAllocateStr(len);
792 for (i = 0; i < values.Length; i++) {
793 if (values[i] == null)
796 InternalStrcpy(tmp, currentpos, values[i]);
797 currentpos += values[i].length;
803 public String Insert(int sindex, String value) {
805 throw new ArgumentNullException();
807 if (sindex < 0 || sindex > this.length)
808 throw new ArgumentOutOfRangeException();
810 return InternalInsert(sindex, value);
814 public static string Intern (string str) {
816 throw new ArgumentNullException ();
818 return InternalIntern(str);
821 public static string IsInterned (string str) {
823 throw new ArgumentNullException();
825 return InternalIsInterned(str);
828 public static string Join (string separator, string [] value) {
830 throw new ArgumentNullException ();
832 return Join(separator, value, 0, value.Length);
835 public static string Join(string separator, string[] value, int sindex, int count) {
837 throw new ArgumentNullException ();
839 if (sindex + count > value.Length)
840 throw new ArgumentOutOfRangeException ();
842 if (sindex == value.Length)
845 return InternalJoin(separator, value, sindex, count);
848 bool IConvertible.ToBoolean (IFormatProvider provider) {
849 return Convert.ToBoolean (this);
852 byte IConvertible.ToByte (IFormatProvider provider) {
853 return Convert.ToByte (this);
856 char IConvertible.ToChar (IFormatProvider provider) {
857 return Convert.ToChar (this);
860 DateTime IConvertible.ToDateTime (IFormatProvider provider) {
861 return Convert.ToDateTime (this);
864 decimal IConvertible.ToDecimal (IFormatProvider provider) {
865 return Convert.ToDecimal (this);
868 double IConvertible.ToDouble (IFormatProvider provider) {
869 return Convert.ToDouble (this);
872 short IConvertible.ToInt16 (IFormatProvider provider) {
873 return Convert.ToInt16 (this);
876 int IConvertible.ToInt32 (IFormatProvider provider) {
877 return Convert.ToInt32 (this);
880 long IConvertible.ToInt64 (IFormatProvider provider) {
881 return Convert.ToInt64 (this);
884 [CLSCompliant(false)]
885 sbyte IConvertible.ToSByte (IFormatProvider provider) {
886 return Convert.ToSByte (this);
889 float IConvertible.ToSingle (IFormatProvider provider) {
890 return Convert.ToSingle (this);
892 string IConvertible.ToString (IFormatProvider format) {
896 object IConvertible.ToType (Type conversionType, IFormatProvider provider) {
897 return Convert.ToType (this, conversionType, provider);
900 [CLSCompliant(false)]
901 ushort IConvertible.ToUInt16 (IFormatProvider provider) {
902 return Convert.ToUInt16 (this);
905 [CLSCompliant(false)]
906 uint IConvertible.ToUInt32 (IFormatProvider provider) {
907 return Convert.ToUInt32 (this);
910 [CLSCompliant(false)]
911 ulong IConvertible.ToUInt64 (IFormatProvider provider) {
912 return Convert.ToUInt64 (this);
915 TypeCode IConvertible.GetTypeCode () {
916 return TypeCode.String;
925 public CharEnumerator GetEnumerator () {
926 return new CharEnumerator (this);
929 IEnumerator IEnumerable.GetEnumerator () {
930 return new CharEnumerator (this);
933 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width, out bool left_align, out string format) {
934 // parses format specifier of form:
940 // N = argument number (non-negative integer)
942 n = ParseDecimal (str, ref ptr);
944 throw new FormatException ("Input string was not in correct format.");
946 // M = width (non-negative integer)
948 if (str[ptr] == ',') {
949 left_align = (str[++ ptr] == '-');
953 width = ParseDecimal (str, ref ptr);
955 throw new FormatException ("Input string was not in correct format.");
962 // F = argument format (string)
964 if (str[ptr] == ':') {
966 while (str[ptr] != '}')
969 format = str.Substring (start, ptr - start);
974 if (str[ptr ++] != '}')
975 throw new FormatException ("Input string was not in correct format.");
977 catch (IndexOutOfRangeException) {
978 throw new FormatException ("Input string was not in correct format.");
982 private static int ParseDecimal (string str, ref int ptr) {
987 if (c < '0' || '9' < c)
990 n = n * 10 + c - '0';
1001 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1002 private extern static string InternalJoin(string separator, string[] value, int sindex, int count);
1004 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1005 private extern String InternalInsert(int sindex, String value);
1007 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1008 private extern String InternalReplace(char oldChar, char newChar);
1010 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1011 private extern String InternalReplace(String oldValue, String newValue);
1013 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1014 private extern String InternalRemove(int sindex, int count);
1016 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1017 private extern void InternalCopyTo(int sindex, char[] dest, int dindex, int count);
1019 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1020 private extern String[] InternalSplit(char[] separator, int count);
1022 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1023 private extern String InternalTrim(char[] chars, int typ);
1025 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1026 private extern int InternalIndexOf(char value, int sindex, int count);
1028 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1029 private extern int InternalIndexOf(string value, int sindex, int count);
1031 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1032 private extern int InternalIndexOfAny(char [] arr, int sindex, int count);
1034 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1035 private extern int InternalLastIndexOf(char value, int sindex, int count);
1037 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1038 private extern int InternalLastIndexOf(String value, int sindex, int count);
1040 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1041 private extern int InternalLastIndexOfAny(char [] anyOf, int sindex, int count);
1043 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1044 private extern String InternalPad(int width, char chr, bool right);
1046 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1047 private extern String InternalToLower();
1049 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1050 private extern String InternalToUpper();
1052 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1053 private extern static String InternalAllocateStr(int length);
1055 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1056 private extern static void InternalStrcpy(String dest, int destPos, String src);
1058 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1059 private extern static void InternalStrcpy(String dest, int destPos, String src, int startPos, int count);
1061 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1062 private extern static string InternalIntern(string str);
1064 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1065 private extern static string InternalIsInterned(string str);
1067 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1068 private extern static int InternalCompare(String s1, int i1, String s2, int i2, int length, bool inCase);