Encoding code clean up (#12864)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Text / ASCIIEncoding.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System;
6 using System.Diagnostics;
7 using System.Diagnostics.Contracts;
8
9 namespace System.Text
10 {
11     // ASCIIEncoding
12     //
13     // Note that ASCIIEncoding is optimized with no best fit and ? for fallback.
14     // It doesn't come in other flavors.
15     //
16     // Note: ASCIIEncoding is the only encoding that doesn't do best fit (windows has best fit).
17     //
18     // Note: IsAlwaysNormalized remains false because 1/2 the code points are unassigned, so they'd
19     //       use fallbacks, and we cannot guarantee that fallbacks are normalized.
20
21     public class ASCIIEncoding : Encoding
22     {
23         // Allow for devirtualization (see https://github.com/dotnet/coreclr/pull/9230)
24         internal sealed class ASCIIEncodingSealed : ASCIIEncoding { }
25
26         // Used by Encoding.ASCII for lazy initialization
27         // The initialization code will not be run until a static member of the class is referenced
28         internal static readonly ASCIIEncodingSealed s_default = new ASCIIEncodingSealed();
29
30         public ASCIIEncoding() : base(Encoding.CodePageASCII)
31         {
32         }
33
34         internal override void SetDefaultFallbacks()
35         {
36             // For ASCIIEncoding we just use default replacement fallback
37             this.encoderFallback = EncoderFallback.ReplacementFallback;
38             this.decoderFallback = DecoderFallback.ReplacementFallback;
39         }
40
41         // WARNING: GetByteCount(string chars), GetBytes(string chars,...), and GetString(byte[] byteIndex...)
42         // WARNING: have different variable names than EncodingNLS.cs, so this can't just be cut & pasted,
43         // WARNING: or it'll break VB's way of calling these.
44         //
45         // The following methods are copied from EncodingNLS.cs.
46         // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
47         // These should be kept in sync for the following classes:
48         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
49
50         // Returns the number of bytes required to encode a range of characters in
51         // a character array.
52         //
53         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
54         // So if you fix this, fix the others.  Currently those include:
55         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
56         // parent method is safe
57
58         public override unsafe int GetByteCount(char[] chars, int index, int count)
59         {
60             // Validate input parameters
61             if (chars == null)
62                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
63
64             if (index < 0 || count < 0)
65                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
66
67             if (chars.Length - index < count)
68                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
69             Contract.EndContractBlock();
70
71             // If no input, return 0, avoid fixed empty array problem
72             if (count == 0)
73                 return 0;
74
75             // Just call the pointer version
76             fixed (char* pChars = chars)
77                 return GetByteCount(pChars + index, count, null);
78         }
79
80         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
81         // So if you fix this, fix the others.  Currently those include:
82         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
83         // parent method is safe
84
85         public override unsafe int GetByteCount(String chars)
86         {
87             // Validate input
88             if (chars==null)
89                 throw new ArgumentNullException("chars");
90             Contract.EndContractBlock();
91
92             fixed (char* pChars = chars)
93                 return GetByteCount(pChars, chars.Length, null);
94         }
95
96         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
97         // So if you fix this, fix the others.  Currently those include:
98         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
99
100         [CLSCompliant(false)]
101         public override unsafe int GetByteCount(char* chars, int count)
102         {
103             // Validate Parameters
104             if (chars == null)
105                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
106
107             if (count < 0)
108                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
109             Contract.EndContractBlock();
110
111             // Call it with empty encoder
112             return GetByteCount(chars, count, null);
113         }
114
115         // Parent method is safe.
116         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
117         // So if you fix this, fix the others.  Currently those include:
118         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
119
120         public override unsafe int GetBytes(String chars, int charIndex, int charCount,
121                                               byte[] bytes, int byteIndex)
122         {
123             if (chars == null || bytes == null)
124                 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
125
126             if (charIndex < 0 || charCount < 0)
127                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
128
129             if (chars.Length - charIndex < charCount)
130                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCount);
131
132             if (byteIndex < 0 || byteIndex > bytes.Length)
133                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
134             Contract.EndContractBlock();
135
136             int byteCount = bytes.Length - byteIndex;
137
138             // Fixed doesn't like empty byte arrays
139             if (bytes.Length == 0)
140                 bytes = new byte[1];
141
142             fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
143                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
144         }
145
146         // Encodes a range of characters in a character array into a range of bytes
147         // in a byte array. An exception occurs if the byte array is not large
148         // enough to hold the complete encoding of the characters. The
149         // GetByteCount method can be used to determine the exact number of
150         // bytes that will be produced for a given range of characters.
151         // Alternatively, the GetMaxByteCount method can be used to
152         // determine the maximum number of bytes that will be produced for a given
153         // number of characters, regardless of the actual character values.
154         //
155         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
156         // So if you fix this, fix the others.  Currently those include:
157         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
158         // parent method is safe
159
160         public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
161                                                byte[] bytes, int byteIndex)
162         {
163             // Validate parameters
164             if (chars == null || bytes == null)
165                 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
166
167             if (charIndex < 0 || charCount < 0)
168                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
169
170             if (chars.Length - charIndex < charCount)
171                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
172
173             if (byteIndex < 0 || byteIndex > bytes.Length)
174                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
175             Contract.EndContractBlock();
176
177             // If nothing to encode return 0, avoid fixed problem
178             if (charCount == 0)
179                 return 0;
180
181             // Just call pointer version
182             int byteCount = bytes.Length - byteIndex;
183
184             // Fixed doesn't like empty byte arrays
185             if (bytes.Length == 0)
186                 bytes = new byte[1];
187
188             fixed (char* pChars = chars)  fixed (byte* pBytes = &bytes[0])
189                 // Remember that byteCount is # to decode, not size of array.
190                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
191         }
192
193         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
194         // So if you fix this, fix the others.  Currently those include:
195         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
196
197         [CLSCompliant(false)]
198         public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
199         {
200             // Validate Parameters
201             if (bytes == null || chars == null)
202                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
203
204             if (charCount < 0 || byteCount < 0)
205                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
206             Contract.EndContractBlock();
207
208             return GetBytes(chars, charCount, bytes, byteCount, null);
209         }
210
211         // Returns the number of characters produced by decoding a range of bytes
212         // in a byte array.
213         //
214         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
215         // So if you fix this, fix the others.  Currently those include:
216         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
217         // parent method is safe
218
219         public override unsafe int GetCharCount(byte[] bytes, int index, int count)
220         {
221             // Validate Parameters
222             if (bytes == null)
223                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
224
225             if (index < 0 || count < 0)
226                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
227
228             if (bytes.Length - index < count)
229                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
230             Contract.EndContractBlock();
231
232             // If no input just return 0, fixed doesn't like 0 length arrays
233             if (count == 0)
234                 return 0;
235
236             // Just call pointer version
237             fixed (byte* pBytes = bytes)
238                 return GetCharCount(pBytes + index, count, null);
239         }
240
241         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
242         // So if you fix this, fix the others.  Currently those include:
243         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
244
245         [CLSCompliant(false)]
246         public override unsafe int GetCharCount(byte* bytes, int count)
247         {
248             // Validate Parameters
249             if (bytes == null)
250                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
251
252             if (count < 0)
253                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
254             Contract.EndContractBlock();
255
256             return GetCharCount(bytes, count, null);
257         }
258
259         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
260         // So if you fix this, fix the others.  Currently those include:
261         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
262         // parent method is safe
263
264         public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
265                                               char[] chars, int charIndex)
266         {
267             // Validate Parameters
268             if (bytes == null || chars == null)
269                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
270
271             if (byteIndex < 0 || byteCount < 0)
272                 throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
273
274             if ( bytes.Length - byteIndex < byteCount)
275                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
276
277             if (charIndex < 0 || charIndex > chars.Length)
278                 throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
279             Contract.EndContractBlock();
280
281             // If no input, return 0 & avoid fixed problem
282             if (byteCount == 0)
283                 return 0;
284
285             // Just call pointer version
286             int charCount = chars.Length - charIndex;
287
288             // Fixed doesn't like empty char arrays
289             if (chars.Length == 0)
290                 chars = new char[1];
291
292             fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
293                 // Remember that charCount is # to decode, not size of array
294                 return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
295         }
296
297         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
298         // So if you fix this, fix the others.  Currently those include:
299         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
300
301         [CLSCompliant(false)]
302         public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
303         {
304             // Validate Parameters
305             if (bytes == null || chars == null)
306                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
307
308             if (charCount < 0 || byteCount < 0)
309                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
310             Contract.EndContractBlock();
311
312             return GetChars(bytes, byteCount, chars, charCount, null);
313         }
314
315         // Returns a string containing the decoded representation of a range of
316         // bytes in a byte array.
317         //
318         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
319         // So if you fix this, fix the others.  Currently those include:
320         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
321         // parent method is safe
322
323         public override unsafe String GetString(byte[] bytes, int byteIndex, int byteCount)
324         {
325             // Validate Parameters
326             if (bytes == null)
327                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
328
329             if (byteIndex < 0 || byteCount < 0)
330                 throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
331
332
333             if (bytes.Length - byteIndex < byteCount)
334                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
335             Contract.EndContractBlock();
336
337             // Avoid problems with empty input buffer
338             if (byteCount == 0) return String.Empty;
339
340             fixed (byte* pBytes = bytes)
341                 return String.CreateStringFromEncoding(
342                     pBytes + byteIndex, byteCount, this);
343         }
344
345         //
346         // End of standard methods copied from EncodingNLS.cs
347         //
348
349         // GetByteCount
350         // Note: We start by assuming that the output will be the same as count.  Having
351         // an encoder or fallback may change that assumption
352         internal override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder)
353         {
354             // Just need to ASSERT, this is called by something else internal that checked parameters already
355             Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetByteCount]count is negative");
356             Debug.Assert(chars != null, "[ASCIIEncoding.GetByteCount]chars is null");
357
358             // Assert because we shouldn't be able to have a null encoder.
359             Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetByteCount]Attempting to use null fallback encoder");
360
361             char charLeftOver = (char)0;
362             EncoderReplacementFallback fallback = null;
363
364             // Start by assuming default count, then +/- for fallback characters
365             char* charEnd = chars + charCount;
366
367             // For fallback we may need a fallback buffer, we know we aren't default fallback.
368             EncoderFallbackBuffer fallbackBuffer = null;
369             char* charsForFallback;
370
371             if (encoder != null)
372             {
373                 charLeftOver = encoder._charLeftOver;
374                 Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
375                     "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate");
376
377                 fallback = encoder.Fallback as EncoderReplacementFallback;
378
379                 // We mustn't have left over fallback data when counting
380                 if (encoder.InternalHasFallbackBuffer)
381                 {
382                     // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
383                     fallbackBuffer = encoder.FallbackBuffer;
384                     if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow)
385                         throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
386
387                     // Set our internal fallback interesting things.
388                     fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
389                 }
390
391                 // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert
392                 Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
393                     encoder.FallbackBuffer.Remaining == 0,
394                     "[ASCIICodePageEncoding.GetByteCount]Expected empty fallback buffer");
395             }
396             else
397             {
398                 fallback = this.EncoderFallback as EncoderReplacementFallback;
399             }
400
401             // If we have an encoder AND we aren't using default fallback,
402             // then we may have a complicated count.
403             if (fallback != null && fallback.MaxCharCount == 1)
404             {
405                 // Replacement fallback encodes surrogate pairs as two ?? (or two whatever), so return size is always
406                 // same as input size.
407                 // Note that no existing SBCS code pages map code points to supplimentary characters, so this is easy.
408
409                 // We could however have 1 extra byte if the last call had an encoder and a funky fallback and
410                 // if we don't use the funky fallback this time.
411
412                 // Do we have an extra char left over from last time?
413                 if (charLeftOver > 0)
414                     charCount++;
415
416                 return (charCount);
417             }
418
419             // Count is more complicated if you have a funky fallback
420             // For fallback we may need a fallback buffer, we know we're not default fallback
421             int byteCount = 0;
422
423             // We may have a left over character from last time, try and process it.
424             if (charLeftOver > 0)
425             {
426                 Debug.Assert(Char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate");
427                 Debug.Assert(encoder != null, "[ASCIIEncoding.GetByteCount]Expected encoder");
428
429                 // Since left over char was a surrogate, it'll have to be fallen back.
430                 // Get Fallback
431                 fallbackBuffer = encoder.FallbackBuffer;
432                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
433
434                 // This will fallback a pair if *chars is a low surrogate
435                 charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
436                 fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
437                 chars = charsForFallback;
438             }
439
440             // Now we may have fallback char[] already from the encoder
441
442             // Go ahead and do it, including the fallback.
443             char ch;
444             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 ||
445                     chars < charEnd)
446             {
447                 // First unwind any fallback
448                 if (ch == 0)
449                 {
450                     // No fallback, just get next char
451                     ch = *chars;
452                     chars++;
453                 }
454
455                 // Check for fallback, this'll catch surrogate pairs too.
456                 // no chars >= 0x80 are allowed.
457                 if (ch > 0x7f)
458                 {
459                     if (fallbackBuffer == null)
460                     {
461                         // Initialize the buffer
462                         if (encoder == null)
463                             fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
464                         else
465                             fallbackBuffer = encoder.FallbackBuffer;
466                         fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, false);
467                     }
468
469                     // Get Fallback
470                     charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
471                     fallbackBuffer.InternalFallback(ch, ref charsForFallback);
472                     chars = charsForFallback;
473                     continue;
474                 }
475
476                 // We'll use this one
477                 byteCount++;
478             }
479
480             Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
481                 "[ASCIIEncoding.GetByteCount]Expected Empty fallback buffer");
482
483             return byteCount;
484         }
485
486         internal override unsafe int GetBytes(char* chars, int charCount,
487                                                 byte* bytes, int byteCount, EncoderNLS encoder)
488         {
489             // Just need to ASSERT, this is called by something else internal that checked parameters already
490             Debug.Assert(bytes != null, "[ASCIIEncoding.GetBytes]bytes is null");
491             Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetBytes]byteCount is negative");
492             Debug.Assert(chars != null, "[ASCIIEncoding.GetBytes]chars is null");
493             Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetBytes]charCount is negative");
494
495             // Assert because we shouldn't be able to have a null encoder.
496             Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetBytes]Attempting to use null encoder fallback");
497
498             // Get any left over characters
499             char charLeftOver = (char)0;
500             EncoderReplacementFallback fallback = null;
501
502             // For fallback we may need a fallback buffer, we know we aren't default fallback.
503             EncoderFallbackBuffer fallbackBuffer = null;
504             char* charsForFallback;
505
506             // prepare our end
507             char* charEnd = chars + charCount;
508             byte* byteStart = bytes;
509             char* charStart = chars;
510
511             if (encoder != null)
512             {
513                 charLeftOver = encoder._charLeftOver;
514                 fallback = encoder.Fallback as EncoderReplacementFallback;
515
516                 // We mustn't have left over fallback data when counting
517                 if (encoder.InternalHasFallbackBuffer)
518                 {
519                     // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
520                     fallbackBuffer = encoder.FallbackBuffer;
521                     if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow)
522                         throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
523
524                     // Set our internal fallback interesting things.
525                     fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
526                 }
527
528                 Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
529                     "[ASCIIEncoding.GetBytes]leftover character should be high surrogate");
530
531                 // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert
532                 Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
533                     encoder.FallbackBuffer.Remaining == 0,
534                     "[ASCIICodePageEncoding.GetBytes]Expected empty fallback buffer");
535             }
536             else
537             {
538                 fallback = this.EncoderFallback as EncoderReplacementFallback;
539             }
540
541
542             // See if we do the fast default or slightly slower fallback
543             if (fallback != null && fallback.MaxCharCount == 1)
544             {
545                 // Fast version
546                 char cReplacement = fallback.DefaultString[0];
547
548                 // Check for replacements in range, otherwise fall back to slow version.
549                 if (cReplacement <= (char)0x7f)
550                 {
551                     // We should have exactly as many output bytes as input bytes, unless there's a left
552                     // over character, in which case we may need one more.
553                     // If we had a left over character will have to add a ?  (This happens if they had a funky
554                     // fallback last time, but not this time.) (We can't spit any out though
555                     // because with fallback encoder each surrogate is treated as a seperate code point)
556                     if (charLeftOver > 0)
557                     {
558                         // Have to have room
559                         // Throw even if doing no throw version because this is just 1 char,
560                         // so buffer will never be big enough
561                         if (byteCount == 0)
562                             ThrowBytesOverflow(encoder, true);
563
564                         // This'll make sure we still have more room and also make sure our return value is correct.
565                         *(bytes++) = (byte)cReplacement;
566                         byteCount--;                // We used one of the ones we were counting.
567                     }
568
569                     // This keeps us from overrunning our output buffer
570                     if (byteCount < charCount)
571                     {
572                         // Throw or make buffer smaller?
573                         ThrowBytesOverflow(encoder, byteCount < 1);
574
575                         // Just use what we can
576                         charEnd = chars + byteCount;
577                     }
578
579                     // We just do a quick copy
580                     while (chars < charEnd)
581                     {
582                         char ch2 = *(chars++);
583                         if (ch2 >= 0x0080) *(bytes++) = (byte)cReplacement;
584                         else *(bytes++) = unchecked((byte)(ch2));
585                     }
586
587                     // Clear encoder
588                     if (encoder != null)
589                     {
590                         encoder._charLeftOver = (char)0;
591                         encoder._charsUsed = (int)(chars - charStart);
592                     }
593
594                     return (int)(bytes - byteStart);
595                 }
596             }
597
598             // Slower version, have to do real fallback.
599
600             // prepare our end
601             byte* byteEnd = bytes + byteCount;
602
603             // We may have a left over character from last time, try and process it.
604             if (charLeftOver > 0)
605             {
606                 // Initialize the buffer
607                 Debug.Assert(encoder != null,
608                     "[ASCIIEncoding.GetBytes]Expected non null encoder if we have surrogate left over");
609                 fallbackBuffer = encoder.FallbackBuffer;
610                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true);
611
612                 // Since left over char was a surrogate, it'll have to be fallen back.
613                 // Get Fallback
614                 // This will fallback a pair if *chars is a low surrogate
615                 charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
616                 fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
617                 chars = charsForFallback;
618             }
619
620             // Now we may have fallback char[] already from the encoder
621
622             // Go ahead and do it, including the fallback.
623             char ch;
624             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 ||
625                     chars < charEnd)
626             {
627                 // First unwind any fallback
628                 if (ch == 0)
629                 {
630                     // No fallback, just get next char
631                     ch = *chars;
632                     chars++;
633                 }
634
635                 // Check for fallback, this'll catch surrogate pairs too.
636                 // All characters >= 0x80 must fall back.
637                 if (ch > 0x7f)
638                 {
639                     // Initialize the buffer
640                     if (fallbackBuffer == null)
641                     {
642                         if (encoder == null)
643                             fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
644                         else
645                             fallbackBuffer = encoder.FallbackBuffer;
646                         fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
647                     }
648
649                     // Get Fallback
650                     charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
651                     fallbackBuffer.InternalFallback(ch, ref charsForFallback);
652                     chars = charsForFallback;
653
654                     // Go ahead & continue (& do the fallback)
655                     continue;
656                 }
657
658                 // We'll use this one
659                 // Bounds check
660                 if (bytes >= byteEnd)
661                 {
662                     // didn't use this char, we'll throw or use buffer
663                     if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false)
664                     {
665                         Debug.Assert(chars > charStart || bytes == byteStart,
666                             "[ASCIIEncoding.GetBytes]Expected chars to have advanced already.");
667                         chars--;                                        // don't use last char
668                     }
669                     else
670                         fallbackBuffer.MovePrevious();
671
672                     // Are we throwing or using buffer?
673                     ThrowBytesOverflow(encoder, bytes == byteStart);    // throw?
674                     break;                                              // don't throw, stop
675                 }
676
677                 // Go ahead and add it
678                 *bytes = unchecked((byte)ch);
679                 bytes++;
680             }
681
682             // Need to do encoder stuff
683             if (encoder != null)
684             {
685                 // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
686                 if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder)
687                     // Clear it in case of MustFlush
688                     encoder._charLeftOver = (char)0;
689
690                 // Set our chars used count
691                 encoder._charsUsed = (int)(chars - charStart);
692             }
693
694             Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
695                 (encoder != null && !encoder._throwOnOverflow),
696                 "[ASCIIEncoding.GetBytes]Expected Empty fallback buffer at end");
697
698             return (int)(bytes - byteStart);
699         }
700
701         // This is internal and called by something else,
702         internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder)
703         {
704             // Just assert, we're called internally so these should be safe, checked already
705             Debug.Assert(bytes != null, "[ASCIIEncoding.GetCharCount]bytes is null");
706             Debug.Assert(count >= 0, "[ASCIIEncoding.GetCharCount]byteCount is negative");
707
708             // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using
709             DecoderReplacementFallback fallback = null;
710
711             if (decoder == null)
712                 fallback = this.DecoderFallback as DecoderReplacementFallback;
713             else
714             {
715                 fallback = decoder.Fallback as DecoderReplacementFallback;
716                 Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer ||
717                     decoder.FallbackBuffer.Remaining == 0,
718                     "[ASCIICodePageEncoding.GetCharCount]Expected empty fallback buffer");
719             }
720
721             if (fallback != null && fallback.MaxCharCount == 1)
722             {
723                 // Just return length, SBCS stay the same length because they don't map to surrogate
724                 // pairs and we don't have a decoder fallback.
725
726                 return count;
727             }
728
729             // Only need decoder fallback buffer if not using default replacement fallback, no best fit for ASCII
730             DecoderFallbackBuffer fallbackBuffer = null;
731
732             // Have to do it the hard way.
733             // Assume charCount will be == count
734             int charCount = count;
735             byte[] byteBuffer = new byte[1];
736
737             // Do it our fast way
738             byte* byteEnd = bytes + count;
739
740             // Quick loop
741             while (bytes < byteEnd)
742             {
743                 // Faster if don't use *bytes++;
744                 byte b = *bytes;
745                 bytes++;
746
747                 // If unknown we have to do fallback count
748                 if (b >= 0x80)
749                 {
750                     if (fallbackBuffer == null)
751                     {
752                         if (decoder == null)
753                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
754                         else
755                             fallbackBuffer = decoder.FallbackBuffer;
756                         fallbackBuffer.InternalInitialize(byteEnd - count, null);
757                     }
758
759                     // Use fallback buffer
760                     byteBuffer[0] = b;
761                     charCount--;            // Have to unreserve the one we already allocated for b
762                     charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
763                 }
764             }
765
766             // Fallback buffer must be empty
767             Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
768                 "[ASCIIEncoding.GetCharCount]Expected Empty fallback buffer");
769
770             // Converted sequence is same length as input
771             return charCount;
772         }
773
774         internal override unsafe int GetChars(byte* bytes, int byteCount,
775                                                 char* chars, int charCount, DecoderNLS decoder)
776         {
777             // Just need to ASSERT, this is called by something else internal that checked parameters already
778             Debug.Assert(bytes != null, "[ASCIIEncoding.GetChars]bytes is null");
779             Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetChars]byteCount is negative");
780             Debug.Assert(chars != null, "[ASCIIEncoding.GetChars]chars is null");
781             Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetChars]charCount is negative");
782
783             // Do it fast way if using ? replacement fallback
784             byte* byteEnd = bytes + byteCount;
785             byte* byteStart = bytes;
786             char* charStart = chars;
787
788             // Note: ASCII doesn't do best fit, but we have to fallback if they use something > 0x7f
789             // Only need decoder fallback buffer if not using ? fallback.
790             // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using
791             DecoderReplacementFallback fallback = null;
792             char* charsForFallback;
793
794             if (decoder == null)
795                 fallback = this.DecoderFallback as DecoderReplacementFallback;
796             else
797             {
798                 fallback = decoder.Fallback as DecoderReplacementFallback;
799                 Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer ||
800                     decoder.FallbackBuffer.Remaining == 0,
801                     "[ASCIICodePageEncoding.GetChars]Expected empty fallback buffer");
802             }
803
804             if (fallback != null && fallback.MaxCharCount == 1)
805             {
806                 // Try it the fast way
807                 char replacementChar = fallback.DefaultString[0];
808
809                 // Need byteCount chars, otherwise too small buffer
810                 if (charCount < byteCount)
811                 {
812                     // Need at least 1 output byte, throw if must throw
813                     ThrowCharsOverflow(decoder, charCount < 1);
814
815                     // Not throwing, use what we can
816                     byteEnd = bytes + charCount;
817                 }
818
819                 // Quick loop, just do '?' replacement because we don't have fallbacks for decodings.
820                 while (bytes < byteEnd)
821                 {
822                     byte b = *(bytes++);
823                     if (b >= 0x80)
824                         // This is an invalid byte in the ASCII encoding.
825                         *(chars++) = replacementChar;
826                     else
827                         *(chars++) = unchecked((char)b);
828                 }
829
830                 // bytes & chars used are the same
831                 if (decoder != null)
832                     decoder._bytesUsed = (int)(bytes - byteStart);
833                 return (int)(chars - charStart);
834             }
835
836             // Slower way's going to need a fallback buffer
837             DecoderFallbackBuffer fallbackBuffer = null;
838             byte[] byteBuffer = new byte[1];
839             char* charEnd = chars + charCount;
840
841             // Not quite so fast loop
842             while (bytes < byteEnd)
843             {
844                 // Faster if don't use *bytes++;
845                 byte b = *(bytes);
846                 bytes++;
847
848                 if (b >= 0x80)
849                 {
850                     // This is an invalid byte in the ASCII encoding.
851                     if (fallbackBuffer == null)
852                     {
853                         if (decoder == null)
854                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
855                         else
856                             fallbackBuffer = decoder.FallbackBuffer;
857                         fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd);
858                     }
859
860                     // Use fallback buffer
861                     byteBuffer[0] = b;
862
863                     // Note that chars won't get updated unless this succeeds
864                     charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
865                     bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
866                     chars = charsForFallback;
867
868                     if (!fallbackResult)
869                     {
870                         // May or may not throw, but we didn't get this byte
871                         Debug.Assert(bytes > byteStart || chars == charStart,
872                             "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (fallback case)");
873                         bytes--;                                            // unused byte
874                         fallbackBuffer.InternalReset();                     // Didn't fall this back
875                         ThrowCharsOverflow(decoder, chars == charStart);    // throw?
876                         break;                                              // don't throw, but stop loop
877                     }
878                 }
879                 else
880                 {
881                     // Make sure we have buffer space
882                     if (chars >= charEnd)
883                     {
884                         Debug.Assert(bytes > byteStart || chars == charStart,
885                             "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (normal case)");
886                         bytes--;                                            // unused byte
887                         ThrowCharsOverflow(decoder, chars == charStart);    // throw?
888                         break;                                              // don't throw, but stop loop
889                     }
890
891                     *(chars) = unchecked((char)b);
892                     chars++;
893                 }
894             }
895
896             // Might have had decoder fallback stuff.
897             if (decoder != null)
898                 decoder._bytesUsed = (int)(bytes - byteStart);
899
900             // Expect Empty fallback buffer for GetChars
901             Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
902                 "[ASCIIEncoding.GetChars]Expected Empty fallback buffer");
903
904             return (int)(chars - charStart);
905         }
906
907
908         public override int GetMaxByteCount(int charCount)
909         {
910             if (charCount < 0)
911                 throw new ArgumentOutOfRangeException(nameof(charCount),
912                      SR.ArgumentOutOfRange_NeedNonNegNum);
913             Contract.EndContractBlock();
914
915             // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
916             long byteCount = (long)charCount + 1;
917
918             if (EncoderFallback.MaxCharCount > 1)
919                 byteCount *= EncoderFallback.MaxCharCount;
920
921             // 1 to 1 for most characters.  Only surrogates with fallbacks have less.
922
923             if (byteCount > 0x7fffffff)
924                 throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
925             return (int)byteCount;
926         }
927
928
929         public override int GetMaxCharCount(int byteCount)
930         {
931             if (byteCount < 0)
932                 throw new ArgumentOutOfRangeException(nameof(byteCount),
933                      SR.ArgumentOutOfRange_NeedNonNegNum);
934             Contract.EndContractBlock();
935
936             // Just return length, SBCS stay the same length because they don't map to surrogate
937             long charCount = (long)byteCount;
938
939             // 1 to 1 for most characters.  Only surrogates with fallbacks have less, unknown fallbacks could be longer.
940             if (DecoderFallback.MaxCharCount > 1)
941                 charCount *= DecoderFallback.MaxCharCount;
942
943             if (charCount > 0x7fffffff)
944                 throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
945
946             return (int)charCount;
947         }
948
949         // True if and only if the encoding only uses single byte code points.  (Ie, ASCII, 1252, etc)
950
951         public override bool IsSingleByte
952         {
953             get
954             {
955                 return true;
956             }
957         }
958
959         public override Decoder GetDecoder()
960         {
961             return new DecoderNLS(this);
962         }
963
964
965         public override Encoder GetEncoder()
966         {
967             return new EncoderNLS(this);
968         }
969     }
970 }