Fix StyleCop warning SA1008 (opening paren spacing)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Text / ASCIIEncoding.cs
blob4a89a6c5f0154f644196d6b5d12f63d51130a14a
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.
5 using System.Buffers;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
10 namespace System.Text
12 // ASCIIEncoding
14 // Note that ASCIIEncoding is optimized with no best fit and ? for fallback.
15 // It doesn't come in other flavors.
17 // Note: ASCIIEncoding is the only encoding that doesn't do best fit (windows has best fit).
19 // Note: IsAlwaysNormalized remains false because 1/2 the code points are unassigned, so they'd
20 // use fallbacks, and we cannot guarantee that fallbacks are normalized.
22 public partial class ASCIIEncoding : Encoding
24 // This specialized sealed type has two benefits:
25 // 1) it allows for devirtualization (see https://github.com/dotnet/coreclr/pull/9230), and
26 // 2) it allows us to provide highly optimized implementations of certain routines because
27 // we can make assumptions about the fallback mechanisms in use (in particular, always
28 // replace with "?").
30 // (We don't take advantage of #2 yet, but we can do so in the future because the implementation
31 // of cloning below allows us to make assumptions about the behaviors of the sealed type.)
32 internal sealed class ASCIIEncodingSealed : ASCIIEncoding
34 public override object Clone()
36 // The base implementation of Encoding.Clone calls object.MemberwiseClone and marks the new object mutable.
37 // We don't want to do this because it violates the invariants we have set for the sealed type.
38 // Instead, we'll create a new instance of the base ASCIIEncoding type and mark it mutable.
40 return new ASCIIEncoding()
42 IsReadOnly = false
47 // Used by Encoding.ASCII for lazy initialization
48 // The initialization code will not be run until a static member of the class is referenced
49 internal static readonly ASCIIEncodingSealed s_default = new ASCIIEncodingSealed();
51 public ASCIIEncoding() : base(Encoding.CodePageASCII)
55 internal sealed override void SetDefaultFallbacks()
57 // For ASCIIEncoding we just use default replacement fallback
58 this.encoderFallback = EncoderFallback.ReplacementFallback;
59 this.decoderFallback = DecoderFallback.ReplacementFallback;
62 // WARNING: GetByteCount(string chars), GetBytes(string chars,...), and GetString(byte[] byteIndex...)
63 // WARNING: have different variable names than EncodingNLS.cs, so this can't just be cut & pasted,
64 // WARNING: or it'll break VB's way of calling these.
66 // The following methods are copied from EncodingNLS.cs.
67 // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
68 // These should be kept in sync for the following classes:
69 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
71 // Returns the number of bytes required to encode a range of characters in
72 // a character array.
74 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
75 // So if you fix this, fix the others. Currently those include:
76 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
77 // parent method is safe
79 public override unsafe int GetByteCount(char[] chars, int index, int count)
81 // Validate input parameters
83 if (chars is null)
85 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars, ExceptionResource.ArgumentNull_Array);
88 if ((index | count) < 0)
90 ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
93 if (chars!.Length - index < count)
95 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.chars, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
98 fixed (char* pChars = chars)
100 return GetByteCountCommon(pChars + index, count);
104 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
105 // So if you fix this, fix the others. Currently those include:
106 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
107 // parent method is safe
109 public override unsafe int GetByteCount(string chars)
111 // Validate input parameters
113 if (chars is null)
115 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars);
118 fixed (char* pChars = chars)
120 return GetByteCountCommon(pChars, chars!.Length);
124 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
125 // So if you fix this, fix the others. Currently those include:
126 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
128 [CLSCompliant(false)]
129 public override unsafe int GetByteCount(char* chars, int count)
131 // Validate Parameters
133 if (chars == null)
135 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars);
138 if (count < 0)
140 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
143 return GetByteCountCommon(chars, count);
146 public override unsafe int GetByteCount(ReadOnlySpan<char> chars)
148 // It's ok for us to pass null pointers down to the workhorse below.
150 fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
152 return GetByteCountCommon(charsPtr, chars.Length);
156 [MethodImpl(MethodImplOptions.AggressiveInlining)]
157 private unsafe int GetByteCountCommon(char* pChars, int charCount)
159 // Common helper method for all non-EncoderNLS entry points to GetByteCount.
160 // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
162 Debug.Assert(charCount >= 0, "Caller shouldn't specify negative length buffer.");
163 Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
165 // First call into the fast path.
167 int totalByteCount = GetByteCountFast(pChars, charCount, EncoderFallback, out int charsConsumed);
169 if (charsConsumed != charCount)
171 // If there's still data remaining in the source buffer, go down the fallback path.
172 // We need to check for integer overflow since the fallback could change the required
173 // output count in unexpected ways.
175 totalByteCount += GetByteCountWithFallback(pChars, charCount, charsConsumed);
176 if (totalByteCount < 0)
178 ThrowConversionOverflow();
182 return totalByteCount;
185 [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetByteCountCommon
186 private protected sealed override unsafe int GetByteCountFast(char* pChars, int charsLength, EncoderFallback? fallback, out int charsConsumed)
188 // First: Can we short-circuit the entire calculation?
189 // If an EncoderReplacementFallback is in use, all non-ASCII chars
190 // (including surrogate halves) are replaced with the default string.
191 // If the default string consists of a single ASCII value, then we
192 // know there's a 1:1 char->byte transcoding in all cases.
194 int byteCount = charsLength;
196 if (!(fallback is EncoderReplacementFallback replacementFallback
197 && replacementFallback.MaxCharCount == 1
198 && replacementFallback.DefaultString[0] <= 0x7F))
200 // Unrecognized fallback mechanism - count chars manually.
202 byteCount = (int)ASCIIUtility.GetIndexOfFirstNonAsciiChar(pChars, (uint)charsLength);
205 charsConsumed = byteCount;
206 return byteCount;
209 // Parent method is safe.
210 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
211 // So if you fix this, fix the others. Currently those include:
212 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
214 public override unsafe int GetBytes(string chars, int charIndex, int charCount,
215 byte[] bytes, int byteIndex)
217 // Validate Parameters
219 if (chars is null || bytes is null)
221 ThrowHelper.ThrowArgumentNullException(
222 argument: (chars is null) ? ExceptionArgument.chars : ExceptionArgument.bytes,
223 resource: ExceptionResource.ArgumentNull_Array);
226 if ((charIndex | charCount) < 0)
228 ThrowHelper.ThrowArgumentOutOfRangeException(
229 argument: (charIndex < 0) ? ExceptionArgument.charIndex : ExceptionArgument.charCount,
230 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
233 if (chars!.Length - charIndex < charCount)
235 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.chars, ExceptionResource.ArgumentOutOfRange_IndexCount);
238 if ((uint)byteIndex > bytes!.Length)
240 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.byteIndex, ExceptionResource.ArgumentOutOfRange_Index);
243 fixed (char* pChars = chars)
244 fixed (byte* pBytes = bytes)
246 return GetBytesCommon(pChars + charIndex, charCount, pBytes + byteIndex, bytes.Length - byteIndex);
250 // Encodes a range of characters in a character array into a range of bytes
251 // in a byte array. An exception occurs if the byte array is not large
252 // enough to hold the complete encoding of the characters. The
253 // GetByteCount method can be used to determine the exact number of
254 // bytes that will be produced for a given range of characters.
255 // Alternatively, the GetMaxByteCount method can be used to
256 // determine the maximum number of bytes that will be produced for a given
257 // number of characters, regardless of the actual character values.
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
264 public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
265 byte[] bytes, int byteIndex)
267 // Validate parameters
269 if (chars is null || bytes is null)
271 ThrowHelper.ThrowArgumentNullException(
272 argument: (chars is null) ? ExceptionArgument.chars : ExceptionArgument.bytes,
273 resource: ExceptionResource.ArgumentNull_Array);
276 if ((charIndex | charCount) < 0)
278 ThrowHelper.ThrowArgumentOutOfRangeException(
279 argument: (charIndex < 0) ? ExceptionArgument.charIndex : ExceptionArgument.charCount,
280 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
283 if (chars!.Length - charIndex < charCount)
285 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.chars, ExceptionResource.ArgumentOutOfRange_IndexCount);
288 if ((uint)byteIndex > bytes!.Length)
290 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.byteIndex, ExceptionResource.ArgumentOutOfRange_Index);
293 fixed (char* pChars = chars)
294 fixed (byte* pBytes = bytes)
296 return GetBytesCommon(pChars + charIndex, charCount, pBytes + byteIndex, bytes.Length - byteIndex);
300 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
301 // So if you fix this, fix the others. Currently those include:
302 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
304 [CLSCompliant(false)]
305 public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
307 // Validate Parameters
309 if (chars == null || bytes == null)
311 ThrowHelper.ThrowArgumentNullException(
312 argument: (chars is null) ? ExceptionArgument.chars : ExceptionArgument.bytes,
313 resource: ExceptionResource.ArgumentNull_Array);
316 if ((charCount | byteCount) < 0)
318 ThrowHelper.ThrowArgumentOutOfRangeException(
319 argument: (charCount < 0) ? ExceptionArgument.charCount : ExceptionArgument.byteCount,
320 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
323 return GetBytesCommon(chars, charCount, bytes, byteCount);
326 public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
328 // It's ok for us to operate on null / empty spans.
330 fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
331 fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
333 return GetBytesCommon(charsPtr, chars.Length, bytesPtr, bytes.Length);
337 [MethodImpl(MethodImplOptions.AggressiveInlining)]
338 private unsafe int GetBytesCommon(char* pChars, int charCount, byte* pBytes, int byteCount)
340 // Common helper method for all non-EncoderNLS entry points to GetBytes.
341 // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
343 Debug.Assert(charCount >= 0, "Caller shouldn't specify negative length buffer.");
344 Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
345 Debug.Assert(byteCount >= 0, "Caller shouldn't specify negative length buffer.");
346 Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
348 // First call into the fast path.
350 int bytesWritten = GetBytesFast(pChars, charCount, pBytes, byteCount, out int charsConsumed);
352 if (charsConsumed == charCount)
354 // All elements converted - return immediately.
356 return bytesWritten;
358 else
360 // Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
362 return GetBytesWithFallback(pChars, charCount, pBytes, byteCount, charsConsumed, bytesWritten);
366 [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetBytesCommon
367 private protected sealed override unsafe int GetBytesFast(char* pChars, int charsLength, byte* pBytes, int bytesLength, out int charsConsumed)
369 int bytesWritten = (int)ASCIIUtility.NarrowUtf16ToAscii(pChars, pBytes, (uint)Math.Min(charsLength, bytesLength));
371 charsConsumed = bytesWritten;
372 return bytesWritten;
375 private protected sealed override unsafe int GetBytesWithFallback(ReadOnlySpan<char> chars, int originalCharsLength, Span<byte> bytes, int originalBytesLength, EncoderNLS? encoder)
377 // We special-case EncoderReplacementFallback if it's telling us to write a single ASCII char,
378 // since we believe this to be relatively common and we can handle it more efficiently than
379 // the base implementation.
381 if (((encoder is null) ? this.EncoderFallback : encoder.Fallback) is EncoderReplacementFallback replacementFallback
382 && replacementFallback.MaxCharCount == 1
383 && replacementFallback.DefaultString[0] <= 0x7F)
385 byte replacementByte = (byte)replacementFallback.DefaultString[0];
387 int numElementsToConvert = Math.Min(chars.Length, bytes.Length);
388 int idx = 0;
390 fixed (char* pChars = &MemoryMarshal.GetReference(chars))
391 fixed (byte* pBytes = &MemoryMarshal.GetReference(bytes))
393 // In a loop, replace the non-convertible data, then bulk-convert as much as we can.
395 while (idx < numElementsToConvert)
397 pBytes[idx++] = replacementByte;
399 if (idx < numElementsToConvert)
401 idx += (int)ASCIIUtility.NarrowUtf16ToAscii(&pChars[idx], &pBytes[idx], (uint)(numElementsToConvert - idx));
404 Debug.Assert(idx <= numElementsToConvert, "Somehow went beyond bounds of source or destination buffer?");
408 // Slice off how much we consumed / wrote.
410 chars = chars.Slice(numElementsToConvert);
411 bytes = bytes.Slice(numElementsToConvert);
414 // If we couldn't go through our fast fallback mechanism, or if we still have leftover
415 // data because we couldn't consume everything in the loop above, we need to go down the
416 // slow fallback path.
418 if (chars.IsEmpty)
420 return originalBytesLength - bytes.Length; // total number of bytes written
422 else
424 return base.GetBytesWithFallback(chars, originalCharsLength, bytes, originalBytesLength, encoder);
428 // Returns the number of characters produced by decoding a range of bytes
429 // in a byte array.
431 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
432 // So if you fix this, fix the others. Currently those include:
433 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
434 // parent method is safe
436 public override unsafe int GetCharCount(byte[] bytes, int index, int count)
438 // Validate Parameters
440 if (bytes is null)
442 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
445 if ((index | count) < 0)
447 ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
450 if (bytes!.Length - index < count)
452 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
455 fixed (byte* pBytes = bytes)
457 return GetCharCountCommon(pBytes + index, count);
461 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
462 // So if you fix this, fix the others. Currently those include:
463 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
465 [CLSCompliant(false)]
466 public override unsafe int GetCharCount(byte* bytes, int count)
468 // Validate Parameters
470 if (bytes == null)
472 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
475 if (count < 0)
477 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
480 return GetCharCountCommon(bytes, count);
483 public override unsafe int GetCharCount(ReadOnlySpan<byte> bytes)
485 // It's ok for us to pass null pointers down to the workhorse routine.
487 fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
489 return GetCharCountCommon(bytesPtr, bytes.Length);
493 [MethodImpl(MethodImplOptions.AggressiveInlining)]
494 private unsafe int GetCharCountCommon(byte* pBytes, int byteCount)
496 // Common helper method for all non-DecoderNLS entry points to GetCharCount.
497 // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
499 Debug.Assert(byteCount >= 0, "Caller shouldn't specify negative length buffer.");
500 Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
502 // First call into the fast path.
504 int totalCharCount = GetCharCountFast(pBytes, byteCount, DecoderFallback, out int bytesConsumed);
506 if (bytesConsumed != byteCount)
508 // If there's still data remaining in the source buffer, go down the fallback path.
509 // We need to check for integer overflow since the fallback could change the required
510 // output count in unexpected ways.
512 totalCharCount += GetCharCountWithFallback(pBytes, byteCount, bytesConsumed);
513 if (totalCharCount < 0)
515 ThrowConversionOverflow();
519 return totalCharCount;
522 [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetCharCountCommon
523 private protected sealed override unsafe int GetCharCountFast(byte* pBytes, int bytesLength, DecoderFallback? fallback, out int bytesConsumed)
525 // First: Can we short-circuit the entire calculation?
526 // If a DecoderReplacementFallback is in use, all non-ASCII bytes are replaced with
527 // the default string. If the default string consists of a single BMP value, then we
528 // know there's a 1:1 byte->char transcoding in all cases.
530 int charCount = bytesLength;
532 if (!(fallback is DecoderReplacementFallback replacementFallback) || replacementFallback.MaxCharCount != 1)
534 // Unrecognized fallback mechanism - count bytes manually.
536 charCount = (int)ASCIIUtility.GetIndexOfFirstNonAsciiByte(pBytes, (uint)bytesLength);
539 bytesConsumed = charCount;
540 return charCount;
543 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
544 // So if you fix this, fix the others. Currently those include:
545 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
546 // parent method is safe
548 public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
549 char[] chars, int charIndex)
551 // Validate Parameters
553 if (bytes is null || chars is null)
555 ThrowHelper.ThrowArgumentNullException(
556 argument: (bytes is null) ? ExceptionArgument.bytes : ExceptionArgument.chars,
557 resource: ExceptionResource.ArgumentNull_Array);
560 if ((byteIndex | byteCount) < 0)
562 ThrowHelper.ThrowArgumentOutOfRangeException(
563 argument: (byteIndex < 0) ? ExceptionArgument.byteIndex : ExceptionArgument.byteCount,
564 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
567 if (bytes!.Length - byteIndex < byteCount)
569 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
572 if ((uint)charIndex > (uint)chars!.Length)
574 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.charIndex, ExceptionResource.ArgumentOutOfRange_Index);
577 fixed (byte* pBytes = bytes)
578 fixed (char* pChars = chars)
580 return GetCharsCommon(pBytes + byteIndex, byteCount, pChars + charIndex, chars.Length - charIndex);
584 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
585 // So if you fix this, fix the others. Currently those include:
586 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
588 [CLSCompliant(false)]
589 public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
591 // Validate Parameters
593 if (bytes is null || chars is null)
595 ThrowHelper.ThrowArgumentNullException(
596 argument: (bytes is null) ? ExceptionArgument.bytes : ExceptionArgument.chars,
597 resource: ExceptionResource.ArgumentNull_Array);
600 if ((byteCount | charCount) < 0)
602 ThrowHelper.ThrowArgumentOutOfRangeException(
603 argument: (byteCount < 0) ? ExceptionArgument.byteCount : ExceptionArgument.charCount,
604 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
607 return GetCharsCommon(bytes, byteCount, chars, charCount);
610 public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
612 // It's ok for us to pass null pointers down to the workhorse below.
614 fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
615 fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
617 return GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length);
621 [MethodImpl(MethodImplOptions.AggressiveInlining)]
622 private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount)
624 // Common helper method for all non-DecoderNLS entry points to GetChars.
625 // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
627 Debug.Assert(byteCount >= 0, "Caller shouldn't specify negative length buffer.");
628 Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
629 Debug.Assert(charCount >= 0, "Caller shouldn't specify negative length buffer.");
630 Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
632 // First call into the fast path.
634 int charsWritten = GetCharsFast(pBytes, byteCount, pChars, charCount, out int bytesConsumed);
636 if (bytesConsumed == byteCount)
638 // All elements converted - return immediately.
640 return charsWritten;
642 else
644 // Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
646 return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten);
650 [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetCharsCommon
651 private protected sealed override unsafe int GetCharsFast(byte* pBytes, int bytesLength, char* pChars, int charsLength, out int bytesConsumed)
653 int charsWritten = (int)ASCIIUtility.WidenAsciiToUtf16(pBytes, pChars, (uint)Math.Min(bytesLength, charsLength));
655 bytesConsumed = charsWritten;
656 return charsWritten;
659 private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
661 // We special-case DecoderReplacementFallback if it's telling us to write a single BMP char,
662 // since we believe this to be relatively common and we can handle it more efficiently than
663 // the base implementation.
665 if (((decoder is null) ? this.DecoderFallback: decoder.Fallback) is DecoderReplacementFallback replacementFallback
666 && replacementFallback.MaxCharCount == 1)
668 char replacementChar = replacementFallback.DefaultString[0];
670 int numElementsToConvert = Math.Min(bytes.Length, chars.Length);
671 int idx = 0;
673 fixed (byte* pBytes = &MemoryMarshal.GetReference(bytes))
674 fixed (char* pChars = &MemoryMarshal.GetReference(chars))
676 // In a loop, replace the non-convertible data, then bulk-convert as much as we can.
678 while (idx < numElementsToConvert)
680 pChars[idx++] = replacementChar;
682 if (idx < numElementsToConvert)
684 idx += (int)ASCIIUtility.WidenAsciiToUtf16(&pBytes[idx], &pChars[idx], (uint)(numElementsToConvert - idx));
687 Debug.Assert(idx <= numElementsToConvert, "Somehow went beyond bounds of source or destination buffer?");
691 // Slice off how much we consumed / wrote.
693 bytes = bytes.Slice(numElementsToConvert);
694 chars = chars.Slice(numElementsToConvert);
697 // If we couldn't go through our fast fallback mechanism, or if we still have leftover
698 // data because we couldn't consume everything in the loop above, we need to go down the
699 // slow fallback path.
701 if (bytes.IsEmpty)
703 return originalCharsLength - chars.Length; // total number of chars written
705 else
707 return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
711 // Returns a string containing the decoded representation of a range of
712 // bytes in a byte array.
714 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
715 // So if you fix this, fix the others. Currently those include:
716 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
717 // parent method is safe
719 public override unsafe string GetString(byte[] bytes, int byteIndex, int byteCount)
721 // Validate Parameters
723 if (bytes is null)
725 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
728 if ((byteIndex | byteCount) < 0)
730 ThrowHelper.ThrowArgumentOutOfRangeException(
731 argument: (byteIndex < 0) ? ExceptionArgument.byteIndex : ExceptionArgument.byteCount,
732 resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
735 if (bytes!.Length - byteIndex < byteCount)
737 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
740 // Avoid problems with empty input buffer
741 if (byteCount == 0)
742 return string.Empty;
744 fixed (byte* pBytes = bytes)
746 return string.CreateStringFromEncoding(pBytes + byteIndex, byteCount, this);
751 // End of standard methods copied from EncodingNLS.cs
755 // Beginning of methods used by shared fallback logic.
758 internal sealed override bool TryGetByteCount(Rune value, out int byteCount)
760 if (value.IsAscii)
762 byteCount = 1;
763 return true;
765 else
767 byteCount = default;
768 return false;
772 internal sealed override OperationStatus EncodeRune(Rune value, Span<byte> bytes, out int bytesWritten)
774 if (value.IsAscii)
776 if (!bytes.IsEmpty)
778 bytes[0] = (byte)value.Value;
779 bytesWritten = 1;
780 return OperationStatus.Done;
782 else
784 bytesWritten = 0;
785 return OperationStatus.DestinationTooSmall;
788 else
790 bytesWritten = 0;
791 return OperationStatus.InvalidData;
795 internal sealed override OperationStatus DecodeFirstRune(ReadOnlySpan<byte> bytes, out Rune value, out int bytesConsumed)
797 if (!bytes.IsEmpty)
799 byte b = bytes[0];
800 if (b <= 0x7F)
802 // ASCII byte
804 value = new Rune(b);
805 bytesConsumed = 1;
806 return OperationStatus.Done;
808 else
810 // Non-ASCII byte
812 value = Rune.ReplacementChar;
813 bytesConsumed = 1;
814 return OperationStatus.InvalidData;
817 else
819 // No data to decode
821 value = Rune.ReplacementChar;
822 bytesConsumed = 0;
823 return OperationStatus.NeedMoreData;
828 // End of methods used by shared fallback logic.
831 public override int GetMaxByteCount(int charCount)
833 if (charCount < 0)
834 throw new ArgumentOutOfRangeException(nameof(charCount),
835 SR.ArgumentOutOfRange_NeedNonNegNum);
837 // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
838 long byteCount = (long)charCount + 1;
840 if (EncoderFallback.MaxCharCount > 1)
841 byteCount *= EncoderFallback.MaxCharCount;
843 // 1 to 1 for most characters. Only surrogates with fallbacks have less.
845 if (byteCount > 0x7fffffff)
846 throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
847 return (int)byteCount;
851 public override int GetMaxCharCount(int byteCount)
853 if (byteCount < 0)
854 throw new ArgumentOutOfRangeException(nameof(byteCount),
855 SR.ArgumentOutOfRange_NeedNonNegNum);
857 // Just return length, SBCS stay the same length because they don't map to surrogate
858 long charCount = (long)byteCount;
860 // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer.
861 if (DecoderFallback.MaxCharCount > 1)
862 charCount *= DecoderFallback.MaxCharCount;
864 if (charCount > 0x7fffffff)
865 throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
867 return (int)charCount;
870 // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc)
872 public override bool IsSingleByte => true;
874 public override Decoder GetDecoder() => new DecoderNLS(this);
877 public override Encoder GetEncoder() => new EncoderNLS(this);