Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[mono.git] / mcs / class / corlib / ReferenceSources / String.cs
1 //
2 // System.String.cs
3 //
4 // Authors:
5 //   Patrik Torstensson
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)
11 //
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)
15 //
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:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
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.
34 //
35 //
36
37 using System.Runtime.CompilerServices;
38 using System.Text;
39
40 namespace System
41 {
42         partial class String
43         {
44                 public int Length {
45                         get {
46                                 return m_stringLength;
47                         }
48                 }
49
50                 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
51                 {
52                         if (strA == null) {
53                                 return strB == null ? 0 : -1;
54                         }
55                         if (strB == null) {
56                                 return 1;
57                         }
58                         int lengthA = Math.Min (lenA, strA.m_stringLength - indexA);
59                         int lengthB = Math.Min (lenB, strB.m_stringLength - indexB);
60
61                         if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
62                                 return 0;
63
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;
68                                 while (ap < end) {
69                                         if (*ap != *bp)
70                                                 return *ap - *bp;
71                                         ap++;
72                                         bp++;
73                                 }
74                                 return lengthA - lengthB;
75                         }
76                 }
77
78                 public int IndexOf (char value, int startIndex, int count)
79                 {
80                         if (startIndex < 0 || startIndex > this.m_stringLength)
81                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
82                         if (count < 0)
83                                 throw new ArgumentOutOfRangeException ("count", "< 0");
84                         if (startIndex > this.m_stringLength - count)
85                                 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.m_stringLength");
86
87                         if ((startIndex == 0 && this.m_stringLength == 0) || (startIndex == this.m_stringLength) || (count == 0))
88                                 return -1;
89
90                         return IndexOfUnchecked (value, startIndex, count);
91                 }
92
93                 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
94                 {
95                         // It helps JIT compiler to optimize comparison
96                         int value_32 = (int)value;
97
98                         fixed (char* start = &m_firstChar) {
99                                 char* ptr = start + startIndex;
100                                 char* end_ptr = ptr + (count >> 3 << 3);
101
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);
119
120                                         ptr += 8;
121                                 }
122
123                                 end_ptr += count & 0x07;
124                                 while (ptr != end_ptr) {
125                                         if (*ptr == value_32)
126                                                 return (int)(ptr - start);
127
128                                         ptr++;
129                                 }
130                                 return -1;
131                         }
132                 }
133
134                 internal unsafe int IndexOfUnchecked (string value, int startIndex, int count)
135                 {
136                         int valueLen = value.Length;
137                         if (count < valueLen)
138                                 return -1;
139
140                         if (valueLen <= 1) {
141                                 if (valueLen == 1)
142                                         return IndexOfUnchecked (value[0], startIndex, count);
143                                 return startIndex;
144                         }
145
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])
153                                                                 goto NextVal;
154                                                 }
155                                                 return (int)(ap - thisptr);
156                                         }
157                                         NextVal:
158                                         ap++;
159                                 }
160                         }
161                         return -1;
162                 }
163
164                 public int IndexOfAny (char [] anyOf, int startIndex, int count)
165                 {
166                         if (anyOf == null)
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.");
172
173                         return IndexOfAnyUnchecked (anyOf, startIndex, count);
174                 }               
175
176                 unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
177                 {
178                         if (anyOf.Length == 0)
179                                 return -1;
180
181                         if (anyOf.Length == 1)
182                                 return IndexOfUnchecked (anyOf[0], startIndex, count);
183
184                         fixed (char* any = anyOf) {
185                                 int highest = *any;
186                                 int lowest = *any;
187
188                                 char* end_any_ptr = any + anyOf.Length;
189                                 char* any_ptr = any;
190                                 while (++any_ptr != end_any_ptr) {
191                                         if (*any_ptr > highest) {
192                                                 highest = *any_ptr;
193                                                 continue;
194                                         }
195
196                                         if (*any_ptr < lowest)
197                                                 lowest = *any_ptr;
198                                 }
199
200                                 fixed (char* start = &m_firstChar) {
201                                         char* ptr = start + startIndex;
202                                         char* end_ptr = ptr + count;
203
204                                         while (ptr != end_ptr) {
205                                                 if (*ptr > highest || *ptr < lowest) {
206                                                         ptr++;
207                                                         continue;
208                                                 }
209
210                                                 if (*ptr == *any)
211                                                         return (int)(ptr - start);
212
213                                                 any_ptr = any;
214                                                 while (++any_ptr != end_any_ptr) {
215                                                         if (*ptr == *any_ptr)
216                                                                 return (int)(ptr - start);
217                                                 }
218
219                                                 ptr++;
220                                         }
221                                 }
222                         }
223                         return -1;
224                 }
225
226                 public int LastIndexOf (char value, int startIndex, int count)
227                 {
228                         if (this.m_stringLength == 0)
229                                 return -1;
230  
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");
238
239                         return LastIndexOfUnchecked (value, startIndex, count);
240                 }
241
242                 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
243                 {
244                         // It helps JIT compiler to optimize comparison
245                         int value_32 = (int)value;
246
247                         fixed (char* start = &m_firstChar) {
248                                 char* ptr = start + startIndex;
249                                 char* end_ptr = ptr - (count >> 3 << 3);
250
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;
268
269                                         ptr -= 8;
270                                 }
271
272                                 end_ptr -= count & 0x07;
273                                 while (ptr != end_ptr) {
274                                         if (*ptr == value_32)
275                                                 return (int)(ptr - start);
276
277                                         ptr--;
278                                 }
279                                 return -1;
280                         }
281                 }
282
283                 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
284                 {
285                         if (anyOf == null) 
286                                 throw new ArgumentNullException ();
287                         if (this.m_stringLength == 0)
288                                 return -1;
289
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");
296
297                         if (this.m_stringLength == 0)
298                                 return -1;
299
300                         return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
301                 }
302
303                 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
304                 {
305                         if (anyOf.Length == 1)
306                                 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
307
308                         fixed (char* start = &m_firstChar, testStart = anyOf) {
309                                 char* ptr = start + startIndex;
310                                 char* ptrEnd = ptr - count;
311                                 char* test;
312                                 char* testEnd = testStart + anyOf.Length;
313
314                                 while (ptr != ptrEnd) {
315                                         test = testStart;
316                                         while (test != testEnd) {
317                                                 if (*test == *ptr)
318                                                         return (int)(ptr - start);
319                                                 test++;
320                                         }
321                                         ptr--;
322                                 }
323                                 return -1;
324                         }
325                 }
326
327         internal static int nativeCompareOrdinalEx (String strA, int indexA, String strB, int indexB, int count)
328         {
329                 return CompareOrdinalUnchecked (strA, indexA, count, strB, indexB, count);
330         }
331
332                 unsafe String ReplaceInternal (char oldChar, char newChar)
333                 {
334 #if !BOOTSTRAP_BASIC                    
335                         if (this.m_stringLength == 0 || oldChar == newChar)
336                                 return this;
337 #endif
338                         int start_pos = IndexOfUnchecked (oldChar, 0, this.m_stringLength);
339 #if !BOOTSTRAP_BASIC
340                         if (start_pos == -1)
341                                 return this;
342 #endif
343                         if (start_pos < 4)
344                                 start_pos = 0;
345
346                         string tmp = FastAllocateString (m_stringLength);
347                         fixed (char* dest = tmp, src = &m_firstChar) {
348                                 if (start_pos != 0)
349                                         CharCopy (dest, src, start_pos);
350
351                                 char* end_ptr = dest + m_stringLength;
352                                 char* dest_ptr = dest + start_pos;
353                                 char* src_ptr = src + start_pos;
354
355                                 while (dest_ptr != end_ptr) {
356                                         if (*src_ptr == oldChar)
357                                                 *dest_ptr = newChar;
358                                         else
359                                                 *dest_ptr = *src_ptr;
360
361                                         ++src_ptr;
362                                         ++dest_ptr;
363                                 }
364                         }
365                         return tmp;
366                 }
367
368                 public String ReplaceInternal (String oldValue, String newValue)
369                 {
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
372
373                         if (oldValue == null)
374                                 throw new ArgumentNullException ("oldValue");
375
376                         if (oldValue.Length == 0)
377                                 throw new ArgumentException ("oldValue is the empty string.");
378
379                         if (this.Length == 0)
380 #if BOOTSTRAP_BASIC
381                                 throw new NotImplementedException ("BOOTSTRAP_BASIC");
382 #else
383                                 return this;
384 #endif
385                         if (newValue == null)
386                                 newValue = Empty;
387
388                         return ReplaceUnchecked (oldValue, newValue);
389                 }
390
391                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
392                 {
393                         if (oldValue.m_stringLength > m_stringLength)
394 #if BOOTSTRAP_BASIC
395                                 throw new NotImplementedException ("BOOTSTRAP_BASIC");
396 #else
397                                 return this;
398 #endif
399
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
404                         }
405
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);
412                                         if (found < 0)
413                                                 break;
414                                         else {
415                                                 if (count < maxValue)
416                                                         dat[count++] = found;
417                                                 else
418                                                         return ReplaceFallback (oldValue, newValue, maxValue);
419                                         }
420                                         i = found + oldValue.m_stringLength;
421                                 }
422                                 if (count == 0)
423 #if BOOTSTRAP_BASIC
424                                 throw new NotImplementedException ("BOOTSTRAP_BASIC");
425 #else
426                                 return this;
427 #endif
428                                 int nlen = 0;
429                                 checked {
430                                         try {
431                                                 nlen = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * count);
432                                         } catch (OverflowException) {
433                                                 throw new OutOfMemoryException ();
434                                         }
435                                 }
436                                 String tmp = FastAllocateString (nlen);
437
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);
443                                                 curPos += precopy;
444                                                 lastReadPos = dat[j] + oldValue.m_stringLength;
445                                                 CharCopy (dest + curPos, replace, newValue.m_stringLength);
446                                                 curPos += newValue.m_stringLength;
447                                         }
448                                         CharCopy (dest + curPos, source + lastReadPos, m_stringLength - lastReadPos);
449                                 }
450                                 return tmp;
451                         }
452                 }
453
454                 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
455                 {
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);
460                                 if (found < 0) {
461                                         sb.Append (InternalSubString (i, m_stringLength - i));
462                                         break;
463                                 }
464                                 sb.Append (InternalSubString (i, found - i));
465                                 sb.Append (newValue);
466                                 i = found + oldValue.m_stringLength;
467                         }
468                         return sb.ToString ();
469
470                 }
471
472                 unsafe String PadHelper (int totalWidth, char paddingChar, bool isRightPadded)
473                 {
474                         if (totalWidth < 0)
475                                 throw new ArgumentOutOfRangeException ("totalWidth", "Non-negative number required");
476                         if (totalWidth <= m_stringLength)
477 #if BOOTSTRAP_BASIC
478                                 throw new NotImplementedException ("BOOTSTRAP_BASIC");
479 #else                   
480                                 return this;
481 #endif
482                         string result = FastAllocateString (totalWidth);
483
484                         fixed (char *dest = result, src = &m_firstChar) {
485                                 if (isRightPadded) {
486                                         CharCopy (dest, src, m_stringLength);
487                                         char *end = dest + totalWidth;
488                                         char *p = dest + m_stringLength;
489                                         while (p < end) {
490                                                 *p++ = paddingChar;
491                                         }
492                                 } else {
493                                         char *p = dest;
494                                         char *end = p + totalWidth - m_stringLength;
495                                         while (p < end) {
496                                                 *p++ = paddingChar;
497                                         }
498                                         CharCopy (p, src, m_stringLength);
499                                 }
500                         }
501
502                         return result;
503                 }
504
505                 internal bool StartsWithOrdinalUnchecked (String value)
506                 {
507 #if BOOTSTRAP_BASIC
508                         throw new NotImplementedException ("BOOTSTRAP_BASIC");
509 #else
510                         return m_stringLength >= value.m_stringLength && CompareOrdinalUnchecked (this, 0, value.m_stringLength, value, 0, value.m_stringLength) == 0;
511 #endif
512                 }
513
514                 internal unsafe bool IsAscii ()
515                 {
516                         fixed (char* src = &m_firstChar) {
517                                 char* end_ptr = src + m_stringLength;
518                                 char* str_ptr = src;
519
520                                 while (str_ptr != end_ptr) {
521                                         if (*str_ptr >= 0x80)
522                                                 return false;
523
524                                         ++str_ptr;
525                                 }
526                         }
527
528                         return true;
529                 }
530
531                 internal bool IsFastSort ()
532                 {
533                         return false;
534                 }
535
536                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
537                 private extern static string InternalIsInterned (string str);
538
539                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
540                 private extern static string InternalIntern (string str);
541
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];
549                                         dest++;
550                                         src++;
551                                         count--;
552                                 }
553                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
554                                         Buffer.memcpy2 ((byte*)dest, (byte*)src, count * 2);
555                                         return;
556                                 }
557                         }
558                         Buffer.memcpy4 ((byte*)dest, (byte*)src, count * 2);
559                 }
560
561                 #region Runtime method-to-ir dependencies
562
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)
565                 {
566                         if (len < 8) {
567                                 while (len != 0) {
568                                         *dest = (byte)val;
569                                         ++dest;
570                                         --len;
571                                 }
572                                 return;
573                         }
574                         if (val != 0) {
575                                 val = val | (val << 8);
576                                 val = val | (val << 16);
577                         }
578                         // align to 4
579                         int rest = (int)dest & 3;
580                         if (rest != 0) {
581                                 rest = 4 - rest;
582                                 len -= rest;
583                                 do {
584                                         *dest = (byte)val;
585                                         ++dest;
586                                         --rest;
587                                 } while (rest != 0);
588                         }
589                         while (len >= 16) {
590                                 ((int*)dest) [0] = val;
591                                 ((int*)dest) [1] = val;
592                                 ((int*)dest) [2] = val;
593                                 ((int*)dest) [3] = val;
594                                 dest += 16;
595                                 len -= 16;
596                         }
597                         while (len >= 4) {
598                                 ((int*)dest) [0] = val;
599                                 dest += 4;
600                                 len -= 4;
601                         }
602                         // tail bytes
603                         while (len > 0) {
604                                 *dest = (byte)val;
605                                 dest++;
606                                 len--;
607                         }
608                 }
609
610                 static unsafe void memcpy (byte *dest, byte *src, int size)
611                 {
612                         Buffer.Memcpy (dest, src, size);
613                 }
614                 #endregion
615
616                 // Certain constructors are redirected to CreateString methods with
617                 // matching argument list. The this pointer should not be used.
618
619                 private unsafe String CreateString (sbyte* value)
620                 {
621                         if (value == null)
622                                 return Empty;
623
624                         byte* bytes = (byte*) value;
625                         int length = 0;
626
627                         try {
628                                 while (bytes++ [0] != 0)
629                                         length++;
630                         } catch (NullReferenceException) {
631                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
632                 }
633
634                         return CreateString (value, 0, length, null);
635                 }
636
637                 unsafe String CreateString (sbyte* value, int startIndex, int length)
638                 {
639                         return CreateString (value, startIndex, length, null);
640                 }
641
642                 unsafe string CreateString (char *value)
643                 {
644                         if (value == null)
645                                 return Empty;
646                         char *p = value;
647                         int i = 0;
648                         while (*p != 0) {
649                                 ++i;
650                                 ++p;
651                         }
652                         string result = FastAllocateString (i);
653
654                         if (i != 0) {
655                                 fixed (char *dest = result) {
656                                         CharCopy (dest, value, i);
657                                 }
658                         }
659                         return result;
660                 }
661
662                 unsafe string CreateString (char *value, int startIndex, int length)
663                 {
664                         if (length == 0)
665                                 return Empty;
666                         if (value == null)
667                                 throw new ArgumentNullException ("value");
668                         if (startIndex < 0)
669                                 throw new ArgumentOutOfRangeException ("startIndex");
670                         if (length < 0)
671                                 throw new ArgumentOutOfRangeException ("length");
672
673                         string result = FastAllocateString (length);
674
675                         fixed (char *dest = result) {
676                                 CharCopy (dest, value + startIndex, length);
677                         }
678                         return result;
679                 }
680
681                 unsafe string CreateString (char [] val, int startIndex, int length)
682                 {
683                         if (val == null)
684                                 throw new ArgumentNullException ("value");
685                         if (startIndex < 0)
686                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
687                         if (length < 0)
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.");
691                         if (length == 0)
692                                 return Empty;
693
694                         string result = FastAllocateString (length);
695
696                         fixed (char *dest = result, src = val) {
697                                 CharCopy (dest, src + startIndex, length);
698                         }
699                         return result;
700                 }
701
702                 unsafe string CreateString (char [] val)
703                 {
704                         if (val == null || val.Length == 0)
705                                 return Empty;
706                         string result = FastAllocateString (val.Length);
707
708                         fixed (char *dest = result, src = val) {
709                                 CharCopy (dest, src, val.Length);
710                         }
711                         return result;
712                 }
713
714                 unsafe string CreateString (char c, int count)
715                 {
716                         if (count < 0)
717                                 throw new ArgumentOutOfRangeException ("count");
718                         if (count == 0)
719                                 return Empty;
720                         string result = FastAllocateString (count);
721                         fixed (char *dest = result) {
722                                 char *p = dest;
723                                 char *end = p + count;
724                                 while (p < end) {
725                                         *p = c;
726                                         p++;
727                                 }
728                         }
729                         return result;
730                 }
731
732                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
733                 {
734                         if (length < 0)
735                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
736                         if (startIndex < 0)
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.");
740
741                         if (enc == null) {
742                                 if (value == null)
743                                         throw new ArgumentNullException ("value");
744                                 if (length == 0)
745                                         return Empty;
746
747                                 enc = Encoding.Default;
748                         }
749
750                         byte [] bytes = new byte [length];
751
752                         if (length != 0)
753                                 fixed (byte* bytePtr = bytes)
754                                         try {
755                                                 if (value == null)
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.");
760                                         }
761
762                         // GetString () is called even when length == 0
763                         return enc.GetString (bytes);
764                 }
765         }
766 }