Merge pull request #5714 from alexischr/update_bockbuild
[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                         //
330                         // .net does following checks in unmanaged land only which is quite
331                         // wrong as it's not always necessary and argument names don't match
332                         // but we are compatible
333                         //
334                         if (count < 0)
335                                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
336
337                         if (indexA < 0 || indexA > strA.Length)
338                                 throw new ArgumentOutOfRangeException("indexA", Environment.GetResourceString("ArgumentOutOfRange_Index"));
339
340                         if (indexB < 0 || indexB > strB.Length)
341                                 throw new ArgumentOutOfRangeException("indexB", Environment.GetResourceString("ArgumentOutOfRange_Index"));
342
343                         return CompareOrdinalUnchecked (strA, indexA, count, strB, indexB, count);
344         }
345
346                 unsafe String ReplaceInternal (char oldChar, char newChar)
347                 {
348                         if (this.m_stringLength == 0 || oldChar == newChar)
349                                 return this;
350
351                         int start_pos = IndexOfUnchecked (oldChar, 0, this.m_stringLength);
352                         if (start_pos == -1)
353                                 return this;
354
355                         if (start_pos < 4)
356                                 start_pos = 0;
357
358                         string tmp = FastAllocateString (m_stringLength);
359                         fixed (char* dest = tmp, src = &m_firstChar) {
360                                 if (start_pos != 0)
361                                         CharCopy (dest, src, start_pos);
362
363                                 char* end_ptr = dest + m_stringLength;
364                                 char* dest_ptr = dest + start_pos;
365                                 char* src_ptr = src + start_pos;
366
367                                 while (dest_ptr != end_ptr) {
368                                         if (*src_ptr == oldChar)
369                                                 *dest_ptr = newChar;
370                                         else
371                                                 *dest_ptr = *src_ptr;
372
373                                         ++src_ptr;
374                                         ++dest_ptr;
375                                 }
376                         }
377                         return tmp;
378                 }
379
380                 internal String ReplaceInternal (String oldValue, String newValue)
381                 {
382                         // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
383                         // LAMESPEC: Result is undefined if result Length is longer than maximum string Length
384
385                         if (oldValue == null)
386                                 throw new ArgumentNullException ("oldValue");
387
388                         if (oldValue.Length == 0)
389                                 throw new ArgumentException ("oldValue is the empty string.");
390
391                         if (this.Length == 0)
392                                 return this;
393
394                         if (newValue == null)
395                                 newValue = Empty;
396
397                         return ReplaceUnchecked (oldValue, newValue);
398                 }
399
400                 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
401                 {
402                         if (oldValue.m_stringLength > m_stringLength)
403                                 return this;
404
405                         if (oldValue.m_stringLength == 1 && newValue.m_stringLength == 1) {
406                                 return Replace (oldValue[0], newValue[0]);
407                                 // ENHANCE: It would be possible to special case oldValue.m_stringLength == newValue.m_stringLength
408                                 // because the m_stringLength of the result would be this.m_stringLength and m_stringLength calculation unneccesary
409                         }
410
411                         const int maxValue = 200; // Allocate 800 byte maximum
412                         int* dat = stackalloc int[maxValue];
413                         fixed (char* source = &m_firstChar, replace = newValue) {
414                                 int i = 0, count = 0;
415                                 while (i < m_stringLength) {
416                                         int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
417                                         if (found < 0)
418                                                 break;
419                                         else {
420                                                 if (count < maxValue)
421                                                         dat[count++] = found;
422                                                 else
423                                                         return ReplaceFallback (oldValue, newValue, maxValue);
424                                         }
425                                         i = found + oldValue.m_stringLength;
426                                 }
427                                 if (count == 0)
428                                         return this;
429
430                                 int nlen = 0;
431                                 checked {
432                                         try {
433                                                 nlen = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * count);
434                                         } catch (OverflowException) {
435                                                 throw new OutOfMemoryException ();
436                                         }
437                                 }
438                                 String tmp = FastAllocateString (nlen);
439
440                                 int curPos = 0, lastReadPos = 0;
441                                 fixed (char* dest = tmp) {
442                                         for (int j = 0; j < count; j++) {
443                                                 int precopy = dat[j] - lastReadPos;
444                                                 CharCopy (dest + curPos, source + lastReadPos, precopy);
445                                                 curPos += precopy;
446                                                 lastReadPos = dat[j] + oldValue.m_stringLength;
447                                                 CharCopy (dest + curPos, replace, newValue.m_stringLength);
448                                                 curPos += newValue.m_stringLength;
449                                         }
450                                         CharCopy (dest + curPos, source + lastReadPos, m_stringLength - lastReadPos);
451                                 }
452                                 return tmp;
453                         }
454                 }
455
456                 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
457                 {
458                         int lengthEstimate = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * testedCount);
459                         StringBuilder sb = new StringBuilder (lengthEstimate);
460                         for (int i = 0; i < m_stringLength;) {
461                                 int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
462                                 if (found < 0) {
463                                         sb.Append (InternalSubString (i, m_stringLength - i));
464                                         break;
465                                 }
466                                 sb.Append (InternalSubString (i, found - i));
467                                 sb.Append (newValue);
468                                 i = found + oldValue.m_stringLength;
469                         }
470                         return sb.ToString ();
471
472                 }
473
474                 unsafe String PadHelper (int totalWidth, char paddingChar, bool isRightPadded)
475                 {
476                         if (totalWidth < 0)
477                                 throw new ArgumentOutOfRangeException ("totalWidth", "Non-negative number required");
478                         if (totalWidth <= m_stringLength)
479                                 return this;
480
481                         string result = FastAllocateString (totalWidth);
482
483                         fixed (char *dest = result, src = &m_firstChar) {
484                                 if (isRightPadded) {
485                                         CharCopy (dest, src, m_stringLength);
486                                         char *end = dest + totalWidth;
487                                         char *p = dest + m_stringLength;
488                                         while (p < end) {
489                                                 *p++ = paddingChar;
490                                         }
491                                 } else {
492                                         char *p = dest;
493                                         char *end = p + totalWidth - m_stringLength;
494                                         while (p < end) {
495                                                 *p++ = paddingChar;
496                                         }
497                                         CharCopy (p, src, m_stringLength);
498                                 }
499                         }
500
501                         return result;
502                 }
503
504                 internal bool StartsWithOrdinalUnchecked (String value)
505                 {
506                         return m_stringLength >= value.m_stringLength && CompareOrdinalUnchecked (this, 0, value.m_stringLength, value, 0, value.m_stringLength) == 0;
507                 }
508
509                 internal unsafe bool IsAscii ()
510                 {
511                         fixed (char* src = &m_firstChar) {
512                                 char* end_ptr = src + m_stringLength;
513                                 char* str_ptr = src;
514
515                                 while (str_ptr != end_ptr) {
516                                         if (*str_ptr >= 0x80)
517                                                 return false;
518
519                                         ++str_ptr;
520                                 }
521                         }
522
523                         return true;
524                 }
525
526                 internal bool IsFastSort ()
527                 {
528                         return false;
529                 }
530
531                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
532                 private extern static string InternalIsInterned (string str);
533
534                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
535                 private extern static string InternalIntern (string str);
536
537                 internal static unsafe void CharCopy (char *dest, char *src, int count) {
538                         // Same rules as for memcpy, but with the premise that 
539                         // chars can only be aligned to even addresses if their
540                         // enclosing types are correctly aligned
541                         if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
542                                 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
543                                         ((short*)dest) [0] = ((short*)src) [0];
544                                         dest++;
545                                         src++;
546                                         count--;
547                                 }
548                                 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
549                                         Buffer.memcpy2 ((byte*)dest, (byte*)src, count * 2);
550                                         return;
551                                 }
552                         }
553                         Buffer.memcpy4 ((byte*)dest, (byte*)src, count * 2);
554                 }
555
556                 #region Runtime method-to-ir dependencies
557
558                 /* helpers used by the runtime as well as above or eslewhere in corlib */
559                 static unsafe void memset (byte *dest, int val, int len)
560                 {
561                         if (len < 8) {
562                                 while (len != 0) {
563                                         *dest = (byte)val;
564                                         ++dest;
565                                         --len;
566                                 }
567                                 return;
568                         }
569                         if (val != 0) {
570                                 val = val | (val << 8);
571                                 val = val | (val << 16);
572                         }
573                         // align to 4
574                         int rest = (int)dest & 3;
575                         if (rest != 0) {
576                                 rest = 4 - rest;
577                                 len -= rest;
578                                 do {
579                                         *dest = (byte)val;
580                                         ++dest;
581                                         --rest;
582                                 } while (rest != 0);
583                         }
584                         while (len >= 16) {
585                                 ((int*)dest) [0] = val;
586                                 ((int*)dest) [1] = val;
587                                 ((int*)dest) [2] = val;
588                                 ((int*)dest) [3] = val;
589                                 dest += 16;
590                                 len -= 16;
591                         }
592                         while (len >= 4) {
593                                 ((int*)dest) [0] = val;
594                                 dest += 4;
595                                 len -= 4;
596                         }
597                         // tail bytes
598                         while (len > 0) {
599                                 *dest = (byte)val;
600                                 dest++;
601                                 len--;
602                         }
603                 }
604
605                 static unsafe void memcpy (byte *dest, byte *src, int size)
606                 {
607                         Buffer.Memcpy (dest, src, size);
608                 }
609
610                 /* Used by the runtime */
611                 internal static unsafe void bzero (byte *dest, int len) {
612                         memset (dest, 0, len);
613                 }
614
615                 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
616                         ((byte*)dest) [0] = 0;
617                 }
618
619                 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
620                         ((short*)dest) [0] = 0;
621                 }
622
623                 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
624                         ((int*)dest) [0] = 0;
625                 }
626
627                 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
628                         ((long*)dest) [0] = 0;
629                 }
630
631                 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
632                         ((byte*)dest) [0] = ((byte*)src) [0];
633                 }
634
635                 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
636                         ((short*)dest) [0] = ((short*)src) [0];
637                 }
638
639                 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
640                         ((int*)dest) [0] = ((int*)src) [0];
641                 }
642
643                 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
644                         ((long*)dest) [0] = ((long*)src) [0];
645                 }
646
647                 #endregion
648
649                 // Certain constructors are redirected to CreateString methods with
650                 // matching argument list. The this pointer should not be used.
651
652                 private unsafe String CreateString (sbyte* value)
653                 {
654                         if (value == null)
655                                 return Empty;
656
657                         byte* bytes = (byte*) value;
658                         int length = 0;
659
660                         try {
661                                 while (bytes++ [0] != 0)
662                                         length++;
663                         } catch (NullReferenceException) {
664                                 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
665                 }
666
667                         return CreateString (value, 0, length, null);
668                 }
669
670                 unsafe String CreateString (sbyte* value, int startIndex, int length)
671                 {
672                         return CreateString (value, startIndex, length, null);
673                 }
674
675                 unsafe string CreateString (char *value)
676                 {
677                         return CtorCharPtr (value);
678                 }
679
680                 unsafe string CreateString (char *value, int startIndex, int length)
681                 {
682                         return CtorCharPtrStartLength (value, startIndex, length);
683                 }
684
685                 string CreateString (char [] val, int startIndex, int length)
686                 {
687                         return CtorCharArrayStartLength (val, startIndex, length);
688                 }
689
690                 string CreateString (char [] val)
691                 {
692                         return CtorCharArray (val);
693                 }
694
695                 unsafe string CreateString (char c, int count)
696                 {
697                         if (count < 0)
698                                 throw new ArgumentOutOfRangeException ("count");
699                         if (count == 0)
700                                 return Empty;
701                         string result = FastAllocateString (count);
702                         fixed (char *dest = result) {
703                                 char *p = dest;
704                                 char *end = p + count;
705                                 while (p < end) {
706                                         *p = c;
707                                         p++;
708                                 }
709                         }
710                         return result;
711                 }
712
713                 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
714                 {
715                         if (length < 0)
716                                 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
717                         if (startIndex < 0)
718                                 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
719                         if (value + startIndex < value)
720                                 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
721
722                         if (enc == null) {
723                                 if (value == null)
724                                         throw new ArgumentNullException ("value");
725                                 if (length == 0)
726                                         return Empty;
727
728                                 enc = Encoding.Default;
729                         }
730
731                         byte [] bytes = new byte [length];
732
733                         if (length != 0)
734                                 fixed (byte* bytePtr = bytes)
735                                         try {
736                                                 if (value == null)
737                                                         throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
738                                                 memcpy (bytePtr, (byte*) (value + startIndex), length);
739                                         } catch (NullReferenceException) {
740                                                 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
741                                         }
742
743                         // GetString () is called even when length == 0
744                         return enc.GetString (bytes);
745                 }
746         }
747 }