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)
329 return CompareOrdinalUnchecked (strA, indexA, count, strB, indexB, count);
332 unsafe String ReplaceInternal (char oldChar, char newChar)
335 if (this.m_stringLength == 0 || oldChar == newChar)
338 int start_pos = IndexOfUnchecked (oldChar, 0, this.m_stringLength);
346 string tmp = FastAllocateString (m_stringLength);
347 fixed (char* dest = tmp, src = &m_firstChar) {
349 CharCopy (dest, src, start_pos);
351 char* end_ptr = dest + m_stringLength;
352 char* dest_ptr = dest + start_pos;
353 char* src_ptr = src + start_pos;
355 while (dest_ptr != end_ptr) {
356 if (*src_ptr == oldChar)
359 *dest_ptr = *src_ptr;
368 public String ReplaceInternal (String oldValue, String newValue)
370 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
371 // LAMESPEC: Result is undefined if result Length is longer than maximum string Length
373 if (oldValue == null)
374 throw new ArgumentNullException ("oldValue");
376 if (oldValue.Length == 0)
377 throw new ArgumentException ("oldValue is the empty string.");
379 if (this.Length == 0)
381 throw new NotImplementedException ("BOOTSTRAP_BASIC");
385 if (newValue == null)
388 return ReplaceUnchecked (oldValue, newValue);
391 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
393 if (oldValue.m_stringLength > m_stringLength)
395 throw new NotImplementedException ("BOOTSTRAP_BASIC");
400 if (oldValue.m_stringLength == 1 && newValue.m_stringLength == 1) {
401 return Replace (oldValue[0], newValue[0]);
402 // ENHANCE: It would be possible to special case oldValue.m_stringLength == newValue.m_stringLength
403 // because the m_stringLength of the result would be this.m_stringLength and m_stringLength calculation unneccesary
406 const int maxValue = 200; // Allocate 800 byte maximum
407 int* dat = stackalloc int[maxValue];
408 fixed (char* source = &m_firstChar, replace = newValue) {
409 int i = 0, count = 0;
410 while (i < m_stringLength) {
411 int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
415 if (count < maxValue)
416 dat[count++] = found;
418 return ReplaceFallback (oldValue, newValue, maxValue);
420 i = found + oldValue.m_stringLength;
424 throw new NotImplementedException ("BOOTSTRAP_BASIC");
431 nlen = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * count);
432 } catch (OverflowException) {
433 throw new OutOfMemoryException ();
436 String tmp = FastAllocateString (nlen);
438 int curPos = 0, lastReadPos = 0;
439 fixed (char* dest = tmp) {
440 for (int j = 0; j < count; j++) {
441 int precopy = dat[j] - lastReadPos;
442 CharCopy (dest + curPos, source + lastReadPos, precopy);
444 lastReadPos = dat[j] + oldValue.m_stringLength;
445 CharCopy (dest + curPos, replace, newValue.m_stringLength);
446 curPos += newValue.m_stringLength;
448 CharCopy (dest + curPos, source + lastReadPos, m_stringLength - lastReadPos);
454 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
456 int lengthEstimate = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * testedCount);
457 StringBuilder sb = new StringBuilder (lengthEstimate);
458 for (int i = 0; i < m_stringLength;) {
459 int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
461 sb.Append (InternalSubString (i, m_stringLength - i));
464 sb.Append (InternalSubString (i, found - i));
465 sb.Append (newValue);
466 i = found + oldValue.m_stringLength;
468 return sb.ToString ();
472 unsafe String PadHelper (int totalWidth, char paddingChar, bool isRightPadded)
475 throw new ArgumentOutOfRangeException ("totalWidth", "Non-negative number required");
476 if (totalWidth <= m_stringLength)
478 throw new NotImplementedException ("BOOTSTRAP_BASIC");
482 string result = FastAllocateString (totalWidth);
484 fixed (char *dest = result, src = &m_firstChar) {
486 CharCopy (dest, src, m_stringLength);
487 char *end = dest + totalWidth;
488 char *p = dest + m_stringLength;
494 char *end = p + totalWidth - m_stringLength;
498 CharCopy (p, src, m_stringLength);
505 internal bool StartsWithOrdinalUnchecked (String value)
508 throw new NotImplementedException ("BOOTSTRAP_BASIC");
510 return m_stringLength >= value.m_stringLength && CompareOrdinalUnchecked (this, 0, value.m_stringLength, value, 0, value.m_stringLength) == 0;
514 internal unsafe bool IsAscii ()
516 fixed (char* src = &m_firstChar) {
517 char* end_ptr = src + m_stringLength;
520 while (str_ptr != end_ptr) {
521 if (*str_ptr >= 0x80)
531 internal bool IsFastSort ()
536 [MethodImplAttribute (MethodImplOptions.InternalCall)]
537 private extern static string InternalIsInterned (string str);
539 [MethodImplAttribute (MethodImplOptions.InternalCall)]
540 private extern static string InternalIntern (string str);
542 internal static unsafe void CharCopy (char *dest, char *src, int count) {
543 // Same rules as for memcpy, but with the premise that
544 // chars can only be aligned to even addresses if their
545 // enclosing types are correctly aligned
546 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
547 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
548 ((short*)dest) [0] = ((short*)src) [0];
553 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
554 Buffer.memcpy2 ((byte*)dest, (byte*)src, count * 2);
558 Buffer.memcpy4 ((byte*)dest, (byte*)src, count * 2);
561 #region Runtime method-to-ir dependencies
563 /* helpers used by the runtime as well as above or eslewhere in corlib */
564 static unsafe void memset (byte *dest, int val, int len)
575 val = val | (val << 8);
576 val = val | (val << 16);
579 int rest = (int)dest & 3;
590 ((int*)dest) [0] = val;
591 ((int*)dest) [1] = val;
592 ((int*)dest) [2] = val;
593 ((int*)dest) [3] = val;
598 ((int*)dest) [0] = val;
610 static unsafe void memcpy (byte *dest, byte *src, int size)
612 Buffer.Memcpy (dest, src, size);
616 // Certain constructors are redirected to CreateString methods with
617 // matching argument list. The this pointer should not be used.
619 private unsafe String CreateString (sbyte* value)
624 byte* bytes = (byte*) value;
628 while (bytes++ [0] != 0)
630 } catch (NullReferenceException) {
631 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
634 return CreateString (value, 0, length, null);
637 unsafe String CreateString (sbyte* value, int startIndex, int length)
639 return CreateString (value, startIndex, length, null);
642 unsafe string CreateString (char *value)
652 string result = FastAllocateString (i);
655 fixed (char *dest = result) {
656 CharCopy (dest, value, i);
662 unsafe string CreateString (char *value, int startIndex, int length)
667 throw new ArgumentNullException ("value");
669 throw new ArgumentOutOfRangeException ("startIndex");
671 throw new ArgumentOutOfRangeException ("length");
673 string result = FastAllocateString (length);
675 fixed (char *dest = result) {
676 CharCopy (dest, value + startIndex, length);
681 unsafe string CreateString (char [] val, int startIndex, int length)
684 throw new ArgumentNullException ("value");
686 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
688 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
689 if (startIndex > val.Length - length)
690 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
694 string result = FastAllocateString (length);
696 fixed (char *dest = result, src = val) {
697 CharCopy (dest, src + startIndex, length);
702 unsafe string CreateString (char [] val)
704 if (val == null || val.Length == 0)
706 string result = FastAllocateString (val.Length);
708 fixed (char *dest = result, src = val) {
709 CharCopy (dest, src, val.Length);
714 unsafe string CreateString (char c, int count)
717 throw new ArgumentOutOfRangeException ("count");
720 string result = FastAllocateString (count);
721 fixed (char *dest = result) {
723 char *end = p + count;
732 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
735 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
737 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
738 if (value + startIndex < value)
739 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
743 throw new ArgumentNullException ("value");
747 enc = Encoding.Default;
750 byte [] bytes = new byte [length];
753 fixed (byte* bytePtr = bytes)
756 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
757 memcpy (bytePtr, (byte*) (value + startIndex), length);
758 } catch (NullReferenceException) {
759 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
762 // GetString () is called even when length == 0
763 return enc.GetString (bytes);