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.
6 using System
.Diagnostics
;
7 using System
.Runtime
.CompilerServices
;
8 using System
.Runtime
.InteropServices
;
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
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()
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
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
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
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
135 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.chars
);
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
;
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.
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
;
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
);
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.
420 return originalBytesLength
- bytes
.Length
; // total number of bytes written
424 return base.GetBytesWithFallback(chars
, originalCharsLength
, bytes
, originalBytesLength
, encoder
);
428 // Returns the number of characters produced by decoding a range of bytes
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
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
472 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.bytes
, ExceptionResource
.ArgumentNull_Array
);
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
;
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.
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
;
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
);
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.
703 return originalCharsLength
- chars
.Length
; // total number of chars written
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
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
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
)
772 internal sealed override OperationStatus
EncodeRune(Rune
value, Span
<byte> bytes
, out int bytesWritten
)
778 bytes
[0] = (byte)value.Value
;
780 return OperationStatus
.Done
;
785 return OperationStatus
.DestinationTooSmall
;
791 return OperationStatus
.InvalidData
;
795 internal sealed override OperationStatus
DecodeFirstRune(ReadOnlySpan
<byte> bytes
, out Rune
value, out int bytesConsumed
)
806 return OperationStatus
.Done
;
812 value = Rune
.ReplacementChar
;
814 return OperationStatus
.InvalidData
;
821 value = Rune
.ReplacementChar
;
823 return OperationStatus
.NeedMoreData
;
828 // End of methods used by shared fallback logic.
831 public override int GetMaxByteCount(int charCount
)
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
)
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);