6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 // Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Runtime.CompilerServices;
46 return m_stringLength;
50 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
53 return strB == null ? 0 : -1;
58 int lengthA = Math.Min (lenA, strA.m_stringLength - indexA);
59 int lengthB = Math.Min (lenB, strB.m_stringLength - indexB);
61 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
64 fixed (char* aptr = strA, bptr = strB) {
65 char* ap = aptr + indexA;
66 char* end = ap + Math.Min (lengthA, lengthB);
67 char* bp = bptr + indexB;
74 return lengthA - lengthB;
78 public int IndexOf (char value, int startIndex, int count)
80 if (startIndex < 0 || startIndex > this.m_stringLength)
81 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
83 throw new ArgumentOutOfRangeException ("count", "< 0");
84 if (startIndex > this.m_stringLength - count)
85 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.m_stringLength");
87 if ((startIndex == 0 && this.m_stringLength == 0) || (startIndex == this.m_stringLength) || (count == 0))
90 return IndexOfUnchecked (value, startIndex, count);
93 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
95 // It helps JIT compiler to optimize comparison
96 int value_32 = (int)value;
98 fixed (char* start = &m_firstChar) {
99 char* ptr = start + startIndex;
100 char* end_ptr = ptr + (count >> 3 << 3);
102 while (ptr != end_ptr) {
103 if (*ptr == value_32)
104 return (int)(ptr - start);
105 if (ptr[1] == value_32)
106 return (int)(ptr - start + 1);
107 if (ptr[2] == value_32)
108 return (int)(ptr - start + 2);
109 if (ptr[3] == value_32)
110 return (int)(ptr - start + 3);
111 if (ptr[4] == value_32)
112 return (int)(ptr - start + 4);
113 if (ptr[5] == value_32)
114 return (int)(ptr - start + 5);
115 if (ptr[6] == value_32)
116 return (int)(ptr - start + 6);
117 if (ptr[7] == value_32)
118 return (int)(ptr - start + 7);
123 end_ptr += count & 0x07;
124 while (ptr != end_ptr) {
125 if (*ptr == value_32)
126 return (int)(ptr - start);
134 internal unsafe int IndexOfUnchecked (string value, int startIndex, int count)
136 int valueLen = value.Length;
137 if (count < valueLen)
142 return IndexOfUnchecked (value[0], startIndex, count);
146 fixed (char* thisptr = &m_firstChar, valueptr = value) {
147 char* ap = thisptr + startIndex;
148 char* thisEnd = ap + count - valueLen + 1;
149 while (ap != thisEnd) {
150 if (*ap == *valueptr) {
151 for (int i = 1; i < valueLen; i++) {
152 if (ap[i] != valueptr[i])
155 return (int)(ap - thisptr);
164 public int IndexOfAny (char [] anyOf, int startIndex, int count)
167 throw new ArgumentNullException ();
168 if (startIndex < 0 || startIndex > this.m_stringLength)
169 throw new ArgumentOutOfRangeException ();
170 if (count < 0 || startIndex > this.m_stringLength - count)
171 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than m_stringLength of the string.");
173 return IndexOfAnyUnchecked (anyOf, startIndex, count);
176 unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
178 if (anyOf.Length == 0)
181 if (anyOf.Length == 1)
182 return IndexOfUnchecked (anyOf[0], startIndex, count);
184 fixed (char* any = anyOf) {
188 char* end_any_ptr = any + anyOf.Length;
190 while (++any_ptr != end_any_ptr) {
191 if (*any_ptr > highest) {
196 if (*any_ptr < lowest)
200 fixed (char* start = &m_firstChar) {
201 char* ptr = start + startIndex;
202 char* end_ptr = ptr + count;
204 while (ptr != end_ptr) {
205 if (*ptr > highest || *ptr < lowest) {
211 return (int)(ptr - start);
214 while (++any_ptr != end_any_ptr) {
215 if (*ptr == *any_ptr)
216 return (int)(ptr - start);
226 public int LastIndexOf (char value, int startIndex, int count)
228 if (this.m_stringLength == 0)
231 // >= for char (> for string)
232 if ((startIndex < 0) || (startIndex >= this.Length))
233 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
234 if ((count < 0) || (count > this.Length))
235 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
236 if (startIndex - count + 1 < 0)
237 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
239 return LastIndexOfUnchecked (value, startIndex, count);
242 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
244 // It helps JIT compiler to optimize comparison
245 int value_32 = (int)value;
247 fixed (char* start = &m_firstChar) {
248 char* ptr = start + startIndex;
249 char* end_ptr = ptr - (count >> 3 << 3);
251 while (ptr != end_ptr) {
252 if (*ptr == value_32)
253 return (int)(ptr - start);
254 if (ptr[-1] == value_32)
255 return (int)(ptr - start) - 1;
256 if (ptr[-2] == value_32)
257 return (int)(ptr - start) - 2;
258 if (ptr[-3] == value_32)
259 return (int)(ptr - start) - 3;
260 if (ptr[-4] == value_32)
261 return (int)(ptr - start) - 4;
262 if (ptr[-5] == value_32)
263 return (int)(ptr - start) - 5;
264 if (ptr[-6] == value_32)
265 return (int)(ptr - start) - 6;
266 if (ptr[-7] == value_32)
267 return (int)(ptr - start) - 7;
272 end_ptr -= count & 0x07;
273 while (ptr != end_ptr) {
274 if (*ptr == value_32)
275 return (int)(ptr - start);
283 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
286 throw new ArgumentNullException ();
287 if (this.m_stringLength == 0)
290 if ((startIndex < 0) || (startIndex >= this.Length))
291 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
292 if ((count < 0) || (count > this.Length))
293 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
294 if (startIndex - count + 1 < 0)
295 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
297 if (this.m_stringLength == 0)
300 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
303 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
305 if (anyOf.Length == 1)
306 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
308 fixed (char* start = &m_firstChar, testStart = anyOf) {
309 char* ptr = start + startIndex;
310 char* ptrEnd = ptr - count;
312 char* testEnd = testStart + anyOf.Length;
314 while (ptr != ptrEnd) {
316 while (test != testEnd) {
318 return (int)(ptr - start);
327 internal static int nativeCompareOrdinalEx (String strA, int indexA, String strB, int indexB, int count)
330 // .net does following checks in unmanaged land only which is quite
331 // wrong as it's not always necessary and argument names don't match
332 // but we are compatible
335 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
337 if (indexA < 0 || indexA > strA.Length)
338 throw new ArgumentOutOfRangeException("indexA", Environment.GetResourceString("ArgumentOutOfRange_Index"));
340 if (indexB < 0 || indexB > strB.Length)
341 throw new ArgumentOutOfRangeException("indexB", Environment.GetResourceString("ArgumentOutOfRange_Index"));
343 return CompareOrdinalUnchecked (strA, indexA, count, strB, indexB, count);
346 unsafe String ReplaceInternal (char oldChar, char newChar)
349 if (this.m_stringLength == 0 || oldChar == newChar)
352 int start_pos = IndexOfUnchecked (oldChar, 0, this.m_stringLength);
360 string tmp = FastAllocateString (m_stringLength);
361 fixed (char* dest = tmp, src = &m_firstChar) {
363 CharCopy (dest, src, start_pos);
365 char* end_ptr = dest + m_stringLength;
366 char* dest_ptr = dest + start_pos;
367 char* src_ptr = src + start_pos;
369 while (dest_ptr != end_ptr) {
370 if (*src_ptr == oldChar)
373 *dest_ptr = *src_ptr;
382 internal String ReplaceInternal (String oldValue, String newValue)
384 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
385 // LAMESPEC: Result is undefined if result Length is longer than maximum string Length
387 if (oldValue == null)
388 throw new ArgumentNullException ("oldValue");
390 if (oldValue.Length == 0)
391 throw new ArgumentException ("oldValue is the empty string.");
393 if (this.Length == 0)
395 throw new NotImplementedException ("BOOTSTRAP_BASIC");
399 if (newValue == null)
402 return ReplaceUnchecked (oldValue, newValue);
405 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
407 if (oldValue.m_stringLength > m_stringLength)
409 throw new NotImplementedException ("BOOTSTRAP_BASIC");
414 if (oldValue.m_stringLength == 1 && newValue.m_stringLength == 1) {
415 return Replace (oldValue[0], newValue[0]);
416 // ENHANCE: It would be possible to special case oldValue.m_stringLength == newValue.m_stringLength
417 // because the m_stringLength of the result would be this.m_stringLength and m_stringLength calculation unneccesary
420 const int maxValue = 200; // Allocate 800 byte maximum
421 int* dat = stackalloc int[maxValue];
422 fixed (char* source = &m_firstChar, replace = newValue) {
423 int i = 0, count = 0;
424 while (i < m_stringLength) {
425 int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
429 if (count < maxValue)
430 dat[count++] = found;
432 return ReplaceFallback (oldValue, newValue, maxValue);
434 i = found + oldValue.m_stringLength;
438 throw new NotImplementedException ("BOOTSTRAP_BASIC");
445 nlen = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * count);
446 } catch (OverflowException) {
447 throw new OutOfMemoryException ();
450 String tmp = FastAllocateString (nlen);
452 int curPos = 0, lastReadPos = 0;
453 fixed (char* dest = tmp) {
454 for (int j = 0; j < count; j++) {
455 int precopy = dat[j] - lastReadPos;
456 CharCopy (dest + curPos, source + lastReadPos, precopy);
458 lastReadPos = dat[j] + oldValue.m_stringLength;
459 CharCopy (dest + curPos, replace, newValue.m_stringLength);
460 curPos += newValue.m_stringLength;
462 CharCopy (dest + curPos, source + lastReadPos, m_stringLength - lastReadPos);
468 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
470 int lengthEstimate = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * testedCount);
471 StringBuilder sb = new StringBuilder (lengthEstimate);
472 for (int i = 0; i < m_stringLength;) {
473 int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
475 sb.Append (InternalSubString (i, m_stringLength - i));
478 sb.Append (InternalSubString (i, found - i));
479 sb.Append (newValue);
480 i = found + oldValue.m_stringLength;
482 return sb.ToString ();
486 unsafe String PadHelper (int totalWidth, char paddingChar, bool isRightPadded)
489 throw new ArgumentOutOfRangeException ("totalWidth", "Non-negative number required");
490 if (totalWidth <= m_stringLength)
492 throw new NotImplementedException ("BOOTSTRAP_BASIC");
496 string result = FastAllocateString (totalWidth);
498 fixed (char *dest = result, src = &m_firstChar) {
500 CharCopy (dest, src, m_stringLength);
501 char *end = dest + totalWidth;
502 char *p = dest + m_stringLength;
508 char *end = p + totalWidth - m_stringLength;
512 CharCopy (p, src, m_stringLength);
519 internal bool StartsWithOrdinalUnchecked (String value)
522 throw new NotImplementedException ("BOOTSTRAP_BASIC");
524 return m_stringLength >= value.m_stringLength && CompareOrdinalUnchecked (this, 0, value.m_stringLength, value, 0, value.m_stringLength) == 0;
528 internal unsafe bool IsAscii ()
530 fixed (char* src = &m_firstChar) {
531 char* end_ptr = src + m_stringLength;
534 while (str_ptr != end_ptr) {
535 if (*str_ptr >= 0x80)
545 internal bool IsFastSort ()
550 [MethodImplAttribute (MethodImplOptions.InternalCall)]
551 private extern static string InternalIsInterned (string str);
553 [MethodImplAttribute (MethodImplOptions.InternalCall)]
554 private extern static string InternalIntern (string str);
556 internal static unsafe void CharCopy (char *dest, char *src, int count) {
557 // Same rules as for memcpy, but with the premise that
558 // chars can only be aligned to even addresses if their
559 // enclosing types are correctly aligned
560 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
561 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
562 ((short*)dest) [0] = ((short*)src) [0];
567 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
568 Buffer.memcpy2 ((byte*)dest, (byte*)src, count * 2);
572 Buffer.memcpy4 ((byte*)dest, (byte*)src, count * 2);
575 #region Runtime method-to-ir dependencies
577 /* helpers used by the runtime as well as above or eslewhere in corlib */
578 static unsafe void memset (byte *dest, int val, int len)
589 val = val | (val << 8);
590 val = val | (val << 16);
593 int rest = (int)dest & 3;
604 ((int*)dest) [0] = val;
605 ((int*)dest) [1] = val;
606 ((int*)dest) [2] = val;
607 ((int*)dest) [3] = val;
612 ((int*)dest) [0] = val;
624 static unsafe void memcpy (byte *dest, byte *src, int size)
626 Buffer.Memcpy (dest, src, size);
629 /* Used by the runtime */
630 internal static unsafe void bzero (byte *dest, int len) {
631 memset (dest, 0, len);
634 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
635 ((byte*)dest) [0] = 0;
638 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
639 ((short*)dest) [0] = 0;
642 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
643 ((int*)dest) [0] = 0;
646 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
647 ((long*)dest) [0] = 0;
650 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
651 ((byte*)dest) [0] = ((byte*)src) [0];
654 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
655 ((short*)dest) [0] = ((short*)src) [0];
658 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
659 ((int*)dest) [0] = ((int*)src) [0];
662 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
663 ((long*)dest) [0] = ((long*)src) [0];
668 // Certain constructors are redirected to CreateString methods with
669 // matching argument list. The this pointer should not be used.
671 private unsafe String CreateString (sbyte* value)
676 byte* bytes = (byte*) value;
680 while (bytes++ [0] != 0)
682 } catch (NullReferenceException) {
683 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
686 return CreateString (value, 0, length, null);
689 unsafe String CreateString (sbyte* value, int startIndex, int length)
691 return CreateString (value, startIndex, length, null);
694 unsafe string CreateString (char *value)
704 string result = FastAllocateString (i);
707 fixed (char *dest = result) {
708 CharCopy (dest, value, i);
714 unsafe string CreateString (char *value, int startIndex, int length)
719 throw new ArgumentNullException ("value");
721 throw new ArgumentOutOfRangeException ("startIndex");
723 throw new ArgumentOutOfRangeException ("length");
725 string result = FastAllocateString (length);
727 fixed (char *dest = result) {
728 CharCopy (dest, value + startIndex, length);
733 unsafe string CreateString (char [] val, int startIndex, int length)
736 throw new ArgumentNullException ("value");
738 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
740 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
741 if (startIndex > val.Length - length)
742 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
746 string result = FastAllocateString (length);
748 fixed (char *dest = result, src = val) {
749 CharCopy (dest, src + startIndex, length);
754 unsafe string CreateString (char [] val)
756 if (val == null || val.Length == 0)
758 string result = FastAllocateString (val.Length);
760 fixed (char *dest = result, src = val) {
761 CharCopy (dest, src, val.Length);
766 unsafe string CreateString (char c, int count)
769 throw new ArgumentOutOfRangeException ("count");
772 string result = FastAllocateString (count);
773 fixed (char *dest = result) {
775 char *end = p + count;
784 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
787 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
789 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
790 if (value + startIndex < value)
791 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
795 throw new ArgumentNullException ("value");
799 enc = Encoding.Default;
802 byte [] bytes = new byte [length];
805 fixed (byte* bytePtr = bytes)
808 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
809 memcpy (bytePtr, (byte*) (value + startIndex), length);
810 } catch (NullReferenceException) {
811 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
814 // GetString () is called even when length == 0
815 return enc.GetString (bytes);