[corlib] Update public api
[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                 internal 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
615                 /* Used by the runtime */
616                 internal static unsafe void bzero (byte *dest, int len) {
617                         memset (dest, 0, len);
618                 }
619
620                 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
621                         ((byte*)dest) [0] = 0;
622                 }
623
624                 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
625                         ((short*)dest) [0] = 0;
626                 }
627
628                 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
629                         ((int*)dest) [0] = 0;
630                 }
631
632                 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
633                         ((long*)dest) [0] = 0;
634                 }
635
636                 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
637                         ((byte*)dest) [0] = ((byte*)src) [0];
638                 }
639
640                 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
641                         ((short*)dest) [0] = ((short*)src) [0];
642                 }
643
644                 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
645                         ((int*)dest) [0] = ((int*)src) [0];
646                 }
647
648                 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
649                         ((long*)dest) [0] = ((long*)src) [0];
650                 }
651
652                 #endregion
653
654                 // Certain constructors are redirected to CreateString methods with
655                 // matching argument list. The this pointer should not be used.
656
657                 private unsafe String CreateString (sbyte* value)
658                 {
659                         if (value == null)
660                                 return Empty;
661
662                         byte* bytes = (byte*) value;
663                         int length = 0;
664
665                         try {
666                                 while (bytes++ [0] != 0)
667                                         length++;
668                         } catch (NullReferenceException) {
669                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
670                 }
671
672                         return CreateString (value, 0, length, null);
673                 }
674
675                 unsafe String CreateString (sbyte* value, int startIndex, int length)
676                 {
677                         return CreateString (value, startIndex, length, null);
678                 }
679
680                 unsafe string CreateString (char *value)
681                 {
682                         if (value == null)
683                                 return Empty;
684                         char *p = value;
685                         int i = 0;
686                         while (*p != 0) {
687                                 ++i;
688                                 ++p;
689                         }
690                         string result = FastAllocateString (i);
691
692                         if (i != 0) {
693                                 fixed (char *dest = result) {
694                                         CharCopy (dest, value, i);
695                                 }
696                         }
697                         return result;
698                 }
699
700                 unsafe string CreateString (char *value, int startIndex, int length)
701                 {
702                         if (length == 0)
703                                 return Empty;
704                         if (value == null)
705                                 throw new ArgumentNullException ("value");
706                         if (startIndex < 0)
707                                 throw new ArgumentOutOfRangeException ("startIndex");
708                         if (length < 0)
709                                 throw new ArgumentOutOfRangeException ("length");
710
711                         string result = FastAllocateString (length);
712
713                         fixed (char *dest = result) {
714                                 CharCopy (dest, value + startIndex, length);
715                         }
716                         return result;
717                 }
718
719                 unsafe string CreateString (char [] val, int startIndex, int length)
720                 {
721                         if (val == null)
722                                 throw new ArgumentNullException ("value");
723                         if (startIndex < 0)
724                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
725                         if (length < 0)
726                                 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
727                         if (startIndex > val.Length - length)
728                                 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
729                         if (length == 0)
730                                 return Empty;
731
732                         string result = FastAllocateString (length);
733
734                         fixed (char *dest = result, src = val) {
735                                 CharCopy (dest, src + startIndex, length);
736                         }
737                         return result;
738                 }
739
740                 unsafe string CreateString (char [] val)
741                 {
742                         if (val == null || val.Length == 0)
743                                 return Empty;
744                         string result = FastAllocateString (val.Length);
745
746                         fixed (char *dest = result, src = val) {
747                                 CharCopy (dest, src, val.Length);
748                         }
749                         return result;
750                 }
751
752                 unsafe string CreateString (char c, int count)
753                 {
754                         if (count < 0)
755                                 throw new ArgumentOutOfRangeException ("count");
756                         if (count == 0)
757                                 return Empty;
758                         string result = FastAllocateString (count);
759                         fixed (char *dest = result) {
760                                 char *p = dest;
761                                 char *end = p + count;
762                                 while (p < end) {
763                                         *p = c;
764                                         p++;
765                                 }
766                         }
767                         return result;
768                 }
769
770                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
771                 {
772                         if (length < 0)
773                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
774                         if (startIndex < 0)
775                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
776                         if (value + startIndex < value)
777                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
778
779                         if (enc == null) {
780                                 if (value == null)
781                                         throw new ArgumentNullException ("value");
782                                 if (length == 0)
783                                         return Empty;
784
785                                 enc = Encoding.Default;
786                         }
787
788                         byte [] bytes = new byte [length];
789
790                         if (length != 0)
791                                 fixed (byte* bytePtr = bytes)
792                                         try {
793                                                 if (value == null)
794                                                         throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
795                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
796                                         } catch (NullReferenceException) {
797                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
798                                         }
799
800                         // GetString () is called even when length == 0
801                         return enc.GetString (bytes);
802                 }
803         }
804 }