< Summary

Line coverage
1%
Covered lines: 10
Uncovered lines: 695
Coverable lines: 705
Total lines: 1830
Line coverage: 1.4%
Branch coverage
0%
Covered branches: 3
Total branches: 514
Branch coverage: 0.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%110%
.ctor(...)100%22100%
.ctor(...)0%220%
SetDefaultFallbacks()50%2266.66%
GetByteCount(...)0%880%
GetByteCount(...)0%440%
GetByteCount(...)100%110%
GetBytes(...)0%880%
GetBytes(...)0%12120%
GetBytes(...)100%110%
GetCharCount(...)0%880%
GetCharCount(...)100%110%
GetChars(...)0%12120%
GetChars(...)100%110%
GetString(...)0%880%
GetByteCount(...)0%82820%
GetBytes(...)0%1101100%
GetCharCount(...)0%96960%
GetChars(...)0%1241240%
GetEncoder()100%110%
GetDecoder()100%110%
GetPreamble()0%440%
GetMaxByteCount(...)0%440%
GetMaxCharCount(...)0%440%
Equals(...)0%10100%
GetHashCode()0%440%
.ctor(...)100%110%
Reset()0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeEncoding.cs

#LineLine coverage
 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
 4//
 5// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
 6//
 7
 8// This define can be used to turn off the fast loops. Useful for finding whether
 9// the problem is fastloop-specific.
 10#define FASTLOOP
 11
 12using System.Diagnostics;
 13using System.Diagnostics.CodeAnalysis;
 14using System.Runtime.CompilerServices;
 15using System.Runtime.InteropServices;
 16
 17namespace System.Text
 18{
 19    public class UnicodeEncoding : Encoding
 20    {
 21        // Used by Encoding.BigEndianUnicode/Unicode for lazy initialization
 22        // The initialization code will not be run until a static member of the class is referenced
 123        internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark
 124        internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrder
 25
 26        private readonly bool isThrowException;
 27
 28        private readonly bool bigEndian;
 29        private readonly bool byteOrderMark;
 30
 31        // Unicode version 2.0 character size in bytes
 32        public const int CharSize = 2;
 33
 34        public UnicodeEncoding()
 035            : this(false, true)
 36        {
 037        }
 38
 39        public UnicodeEncoding(bool bigEndian, bool byteOrderMark)
 240            : base(bigEndian ? 1201 : 1200)  // Set the data item.
 41        {
 242            this.bigEndian = bigEndian;
 243            this.byteOrderMark = byteOrderMark;
 244        }
 45
 46        public UnicodeEncoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidBytes)
 047            : this(bigEndian, byteOrderMark)
 48        {
 049            this.isThrowException = throwOnInvalidBytes;
 50
 51            // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions
 052            if (this.isThrowException)
 053                SetDefaultFallbacks();
 054        }
 55
 56        internal sealed override void SetDefaultFallbacks()
 57        {
 58            // For UTF-X encodings, we use a replacement fallback with an empty string
 259            if (this.isThrowException)
 60            {
 061                this.encoderFallback = EncoderFallback.ExceptionFallback;
 062                this.decoderFallback = DecoderFallback.ExceptionFallback;
 63            }
 64            else
 65            {
 266                this.encoderFallback = new EncoderReplacementFallback("\xFFFD");
 267                this.decoderFallback = new DecoderReplacementFallback("\xFFFD");
 68            }
 269        }
 70
 71        // The following methods are copied from EncodingNLS.cs.
 72        // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
 73        // These should be kept in sync for the following classes:
 74        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 75        //
 76
 77        // Returns the number of bytes required to encode a range of characters in
 78        // a character array.
 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(char[] chars, int index, int count)
 86        {
 087            ArgumentNullException.ThrowIfNull(chars);
 88
 089            ArgumentOutOfRangeException.ThrowIfNegative(index);
 090            ArgumentOutOfRangeException.ThrowIfNegative(count);
 91
 092            if (chars.Length - index < count)
 093                throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
 94
 95            // If no input, return 0, avoid fixed empty array problem
 096            if (count == 0)
 097                return 0;
 98
 99            // Just call the pointer version
 0100            fixed (char* pChars = chars)
 0101                return GetByteCount(pChars + index, count, null);
 102        }
 103
 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
 108
 109        public override unsafe int GetByteCount(string s)
 110        {
 0111            if (s is null)
 112            {
 0113                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
 114            }
 115
 0116            fixed (char* pChars = s)
 0117                return GetByteCount(pChars, s.Length, null);
 118        }
 119
 120        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 121        // So if you fix this, fix the others.  Currently those include:
 122        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 123
 124        [CLSCompliant(false)]
 125        public override unsafe int GetByteCount(char* chars, int count)
 126        {
 0127            ArgumentNullException.ThrowIfNull(chars);
 128
 0129            ArgumentOutOfRangeException.ThrowIfNegative(count);
 130
 131            // Call it with empty encoder
 0132            return GetByteCount(chars, count, null);
 133        }
 134
 135        // Parent method is safe.
 136        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 137        // So if you fix this, fix the others.  Currently those include:
 138        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 139
 140        public override unsafe int GetBytes(string s, int charIndex, int charCount,
 141                                            byte[] bytes, int byteIndex)
 142        {
 0143            ArgumentNullException.ThrowIfNull(s);
 0144            ArgumentNullException.ThrowIfNull(bytes);
 145
 0146            ArgumentOutOfRangeException.ThrowIfNegative(charIndex);
 0147            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 148
 0149            if (s.Length - charIndex < charCount)
 0150                throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount);
 151
 0152            if (byteIndex < 0 || byteIndex > bytes.Length)
 0153                throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_IndexMustBeLessOrEqual);
 154
 0155            int byteCount = bytes.Length - byteIndex;
 156
 0157            fixed (char* pChars = s)
 0158            fixed (byte* pBytes = &MemoryMarshal.GetArrayDataReference(bytes))
 159            {
 0160                return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
 161            }
 162        }
 163
 164        // Encodes a range of characters in a character array into a range of bytes
 165        // in a byte array. An exception occurs if the byte array is not large
 166        // enough to hold the complete encoding of the characters. The
 167        // GetByteCount method can be used to determine the exact number of
 168        // bytes that will be produced for a given range of characters.
 169        // Alternatively, the GetMaxByteCount method can be used to
 170        // determine the maximum number of bytes that will be produced for a given
 171        // number of characters, regardless of the actual character values.
 172        //
 173        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 174        // So if you fix this, fix the others.  Currently those include:
 175        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 176        // parent method is safe
 177
 178        public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
 179                                            byte[] bytes, int byteIndex)
 180        {
 0181            ArgumentNullException.ThrowIfNull(chars);
 0182            ArgumentNullException.ThrowIfNull(bytes);
 183
 0184            ArgumentOutOfRangeException.ThrowIfNegative(charIndex);
 0185            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 186
 0187            if (chars.Length - charIndex < charCount)
 0188                throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
 189
 0190            if (byteIndex < 0 || byteIndex > bytes.Length)
 0191                throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_IndexMustBeLessOrEqual);
 192
 193            // If nothing to encode return 0, avoid fixed problem
 0194            if (charCount == 0)
 0195                return 0;
 196
 197            // Just call pointer version
 0198            int byteCount = bytes.Length - byteIndex;
 199
 0200            fixed (char* pChars = chars)
 0201            fixed (byte* pBytes = &MemoryMarshal.GetArrayDataReference(bytes))
 202            {
 203                // Remember that byteCount is # to decode, not size of array.
 0204                return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
 205            }
 206        }
 207
 208        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 209        // So if you fix this, fix the others.  Currently those include:
 210        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 211
 212        [CLSCompliant(false)]
 213        public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
 214        {
 0215            ArgumentNullException.ThrowIfNull(chars);
 0216            ArgumentNullException.ThrowIfNull(bytes);
 217
 0218            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 0219            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 220
 0221            return GetBytes(chars, charCount, bytes, byteCount, null);
 222        }
 223
 224        // Returns the number of characters produced by decoding a range of bytes
 225        // in a byte array.
 226        //
 227        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 228        // So if you fix this, fix the others.  Currently those include:
 229        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 230        // parent method is safe
 231
 232        public override unsafe int GetCharCount(byte[] bytes, int index, int count)
 233        {
 0234            ArgumentNullException.ThrowIfNull(bytes);
 235
 0236            ArgumentOutOfRangeException.ThrowIfNegative(index);
 0237            ArgumentOutOfRangeException.ThrowIfNegative(count);
 238
 0239            if (bytes.Length - index < count)
 0240                throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
 241
 242            // If no input just return 0, fixed doesn't like 0 length arrays
 0243            if (count == 0)
 0244                return 0;
 245
 246            // Just call pointer version
 0247            fixed (byte* pBytes = bytes)
 0248                return GetCharCount(pBytes + index, count, null);
 249        }
 250
 251        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 252        // So if you fix this, fix the others.  Currently those include:
 253        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 254
 255        [CLSCompliant(false)]
 256        public override unsafe int GetCharCount(byte* bytes, int count)
 257        {
 0258            ArgumentNullException.ThrowIfNull(bytes);
 259
 0260            ArgumentOutOfRangeException.ThrowIfNegative(count);
 261
 0262            return GetCharCount(bytes, count, null);
 263        }
 264
 265        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 266        // So if you fix this, fix the others.  Currently those include:
 267        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 268        // parent method is safe
 269
 270        public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
 271                                            char[] chars, int charIndex)
 272        {
 0273            ArgumentNullException.ThrowIfNull(bytes);
 0274            ArgumentNullException.ThrowIfNull(chars);
 275
 0276            ArgumentOutOfRangeException.ThrowIfNegative(byteIndex);
 0277            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 278
 0279            if (bytes.Length - byteIndex < byteCount)
 0280                throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
 281
 0282            if (charIndex < 0 || charIndex > chars.Length)
 0283                throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_IndexMustBeLessOrEqual);
 284
 285            // If no input, return 0 & avoid fixed problem
 0286            if (byteCount == 0)
 0287                return 0;
 288
 289            // Just call pointer version
 0290            int charCount = chars.Length - charIndex;
 291
 0292            fixed (byte* pBytes = bytes)
 0293            fixed (char* pChars = &MemoryMarshal.GetArrayDataReference(chars))
 294            {
 295                // Remember that charCount is # to decode, not size of array
 0296                return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
 297            }
 298        }
 299
 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
 303
 304        [CLSCompliant(false)]
 305        public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
 306        {
 0307            ArgumentNullException.ThrowIfNull(bytes);
 0308            ArgumentNullException.ThrowIfNull(chars);
 309
 0310            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 0311            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 312
 0313            return GetChars(bytes, byteCount, chars, charCount, null);
 314        }
 315
 316        // Returns a string containing the decoded representation of a range of
 317        // bytes in a byte array.
 318        //
 319        // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
 320        // So if you fix this, fix the others.  Currently those include:
 321        // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
 322        // parent method is safe
 323
 324        public override unsafe string GetString(byte[] bytes, int index, int count)
 325        {
 0326            ArgumentNullException.ThrowIfNull(bytes);
 327
 0328            ArgumentOutOfRangeException.ThrowIfNegative(index);
 0329            ArgumentOutOfRangeException.ThrowIfNegative(count);
 330
 0331            if (bytes.Length - index < count)
 0332                throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
 333
 334            // Avoid problems with empty input buffer
 0335            if (count == 0) return string.Empty;
 336
 0337            fixed (byte* pBytes = bytes)
 0338                return string.CreateStringFromEncoding(
 0339                    pBytes + index, count, this);
 340        }
 341
 342        //
 343        // End of standard methods copied from EncodingNLS.cs
 344        //
 345        internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS? encoder)
 346        {
 347            Debug.Assert(chars is not null, "[UnicodeEncoding.GetByteCount]chars!=null");
 0348            Debug.Assert(count >= 0, "[UnicodeEncoding.GetByteCount]count >=0");
 349
 350            // Start by assuming each char gets 2 bytes
 0351            int byteCount = count << 1;
 352
 353            // Check for overflow in byteCount
 354            // (If they were all invalid chars, this would actually be wrong,
 355            // but that's a ridiculously large # so we're not concerned about that case)
 0356            if (byteCount < 0)
 0357                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow);
 358
 0359            char* charStart = chars;
 0360            char* charEnd = chars + count;
 0361            char charLeftOver = (char)0;
 362
 0363            bool wasHereBefore = false;
 364
 365            // For fallback we may need a fallback buffer
 0366            EncoderFallbackBuffer? fallbackBuffer = null;
 367            char* charsForFallback;
 368
 0369            if (encoder is not null)
 370            {
 0371                charLeftOver = encoder._charLeftOver;
 372
 373                // Assume extra bytes to encode charLeftOver if it existed
 0374                if (charLeftOver > 0)
 0375                    byteCount += 2;
 376
 377                // We mustn't have left over fallback data when counting
 0378                if (encoder.InternalHasFallbackBuffer)
 379                {
 0380                    fallbackBuffer = encoder.FallbackBuffer;
 0381                    if (fallbackBuffer.Remaining > 0)
 0382                        throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, en
 383
 384                    // Set our internal fallback interesting things.
 0385                    fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 386                }
 387            }
 388
 389            char ch;
 390        TryAgain:
 391
 0392            while (((ch = (fallbackBuffer is null) ? (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) || chars < ch
 393            {
 394                // First unwind any fallback
 0395                if (ch == 0)
 396                {
 397                    // No fallback, maybe we can do it fast
 398#if FASTLOOP
 399                    // If endianness is backwards then each pair of bytes would be backwards.
 0400                    if ((bigEndian ^ BitConverter.IsLittleEndian) &&
 0401#if TARGET_64BIT
 0402                        (unchecked((long)chars) & 7) == 0 &&
 0403#else
 0404                        (unchecked((int)chars) & 3) == 0 &&
 0405#endif
 0406                        charLeftOver == 0)
 407                    {
 408                        // Need -1 to check 2 at a time.  If we have an even #, longChars will go
 409                        // from longEnd - 1/2 long to longEnd + 1/2 long.  If we're odd, longChars
 410                        // will go from longEnd - 1 long to longEnd. (Might not get to use this)
 0411                        ulong* longEnd = (ulong*)(charEnd - 3);
 412
 413                        // Need new char* so we can check 4 at a time
 0414                        ulong* longChars = (ulong*)chars;
 415
 0416                        while (longChars < longEnd)
 417                        {
 418                            // See if we potentially have surrogates (0x8000 bit set)
 419                            // (We're either big endian on a big endian machine or little endian on
 420                            // a little endian machine so that'll work)
 0421                            if ((0x8000800080008000 & *longChars) != 0)
 422                            {
 423                                // See if any of these are high or low surrogates (0xd800 - 0xdfff).  If the high
 424                                // 5 bits looks like 11011, then its a high or low surrogate.
 425                                // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
 426                                // Note that we expect BMP characters to be more common than surrogates
 427                                // & each char with 11111... then ^ with 11011.  Zeroes then indicate surrogates
 0428                                ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
 429
 430                                // Check each of the 4 chars.  0 for those 16 bits means it was a surrogate
 431                                // but no clue if they're high or low.
 432                                // If each of the 4 characters are non-zero, then none are surrogates.
 0433                                if ((uTemp & 0xFFFF000000000000) == 0 ||
 0434                                    (uTemp & 0x0000FFFF00000000) == 0 ||
 0435                                    (uTemp & 0x00000000FFFF0000) == 0 ||
 0436                                    (uTemp & 0x000000000000FFFF) == 0)
 437                                {
 438                                    // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
 439                                    // or if there's 1 or 4 surrogates
 440
 441                                    // If they happen to be high/low/high/low, we may as well continue.  Check the next
 442                                    // bit to see if its set (low) or not (high) in the right pattern
 0443                                    if ((0xfc00fc00fc00fc00 & *longChars) !=
 0444                                            (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00
 445                                    {
 446                                        // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
 447                                        // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
 448
 449                                        // Drop out to the slow loop to resolve the surrogates
 450                                        break;
 451                                    }
 452                                    // else they are all surrogates in High/Low/High/Low order, so we can use them.
 453                                }
 454                                // else none are surrogates, so we can use them.
 455                            }
 456                            // else all < 0x8000 so we can use them
 457
 458                            // We already counted these four chars, go to next long.
 0459                            longChars++;
 460                        }
 461
 0462                        chars = (char*)longChars;
 463
 0464                        if (chars >= charEnd)
 465                            break;
 466                    }
 467#endif // FASTLOOP
 468
 469                    // No fallback, just get next char
 0470                    ch = *chars;
 0471                    chars++;
 472                }
 473                else
 474                {
 475                    // We weren't preallocating fallback space.
 0476                    byteCount += 2;
 477                }
 478
 479                // Check for high or low surrogates
 0480                if (ch >= 0xd800 && ch <= 0xdfff)
 481                {
 482                    // Was it a high surrogate?
 0483                    if (ch <= 0xdbff)
 484                    {
 485                        // Its a high surrogate, if we already had a high surrogate do its fallback
 0486                        if (charLeftOver > 0)
 487                        {
 488                            // Unwind the current character, this should be safe because we
 489                            // don't have leftover data in the fallback, so chars must have
 490                            // advanced already.
 0491                            Debug.Assert(chars > charStart,
 0492                                "[UnicodeEncoding.GetByteCount]Expected chars to have advanced in unexpected high surrog
 0493                            chars--;
 494
 495                            // If previous high surrogate deallocate 2 bytes
 0496                            byteCount -= 2;
 497
 498                            // Fallback the previous surrogate
 499                            // Need to initialize fallback buffer?
 0500                            if (fallbackBuffer is null)
 501                            {
 0502                                fallbackBuffer = encoder is null ?
 0503                                    this.encoderFallback.CreateFallbackBuffer() :
 0504                                    encoder.FallbackBuffer;
 505
 506                                // Set our internal fallback interesting things.
 0507                                fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 508                            }
 509
 0510                            charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
 0511                            fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0512                            chars = charsForFallback;
 513
 514                            // Now no high surrogate left over
 0515                            charLeftOver = (char)0;
 0516                            continue;
 517                        }
 518
 519                        // Remember this high surrogate
 0520                        charLeftOver = ch;
 0521                        continue;
 522                    }
 523
 524                    // Its a low surrogate
 0525                    if (charLeftOver == 0)
 526                    {
 527                        // Expected a previous high surrogate.
 528                        // Don't count this one (we'll count its fallback if necessary)
 0529                        byteCount -= 2;
 530
 531                        // fallback this one
 532                        // Need to initialize fallback buffer?
 0533                        if (fallbackBuffer is null)
 534                        {
 0535                            fallbackBuffer = encoder is null ?
 0536                                this.encoderFallback.CreateFallbackBuffer() :
 0537                                encoder.FallbackBuffer;
 538
 539                            // Set our internal fallback interesting things.
 0540                            fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 541                        }
 0542                        charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0543                        fallbackBuffer.InternalFallback(ch, ref charsForFallback);
 0544                        chars = charsForFallback;
 0545                        continue;
 546                    }
 547
 548                    // Valid surrogate pair, add our charLeftOver
 0549                    charLeftOver = (char)0;
 0550                    continue;
 551                }
 0552                else if (charLeftOver > 0)
 553                {
 554                    // Expected a low surrogate, but this char is normal
 555
 556                    // Rewind the current character, fallback previous character.
 557                    // this should be safe because we don't have leftover data in the
 558                    // fallback, so chars must have advanced already.
 0559                    Debug.Assert(chars > charStart,
 0560                        "[UnicodeEncoding.GetByteCount]Expected chars to have advanced when expected low surrogate");
 0561                    chars--;
 562
 563                    // fallback previous chars
 564                    // Need to initialize fallback buffer?
 0565                    if (fallbackBuffer is null)
 566                    {
 0567                        fallbackBuffer = encoder is null ?
 0568                            this.encoderFallback.CreateFallbackBuffer() :
 0569                            encoder.FallbackBuffer;
 570
 571                        // Set our internal fallback interesting things.
 0572                        fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 573                    }
 0574                    charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0575                    fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0576                    chars = charsForFallback;
 577
 578                    // Ignore charLeftOver or throw
 0579                    byteCount -= 2;
 0580                    charLeftOver = (char)0;
 581
 582                    continue;
 583                }
 584
 585                // Ok we had something to add (already counted)
 586            }
 587
 588            // Don't allocate space for left over char
 0589            if (charLeftOver > 0)
 590            {
 0591                byteCount -= 2;
 592
 593                // If we have to flush, stick it in fallback and try again
 0594                if (encoder is null || encoder.MustFlush)
 595                {
 0596                    if (wasHereBefore)
 597                    {
 598                        // Throw it, using our complete character
 0599                        throw new ArgumentException(
 0600                            SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
 601                    }
 602                    else
 603                    {
 604                        // Need to initialize fallback buffer?
 0605                        if (fallbackBuffer is null)
 606                        {
 0607                            fallbackBuffer = encoder is null ?
 0608                                this.encoderFallback.CreateFallbackBuffer() :
 0609                                encoder.FallbackBuffer;
 610
 611                            // Set our internal fallback interesting things.
 0612                            fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 613                        }
 0614                        charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0615                        fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0616                        chars = charsForFallback;
 0617                        charLeftOver = (char)0;
 0618                        wasHereBefore = true;
 0619                        goto TryAgain;
 620                    }
 621                }
 622            }
 623
 624            // Shouldn't have anything in fallback buffer for GetByteCount
 625            // (don't have to check _throwOnOverflow for count)
 0626            Debug.Assert(fallbackBuffer is null || fallbackBuffer.Remaining == 0,
 0627                "[UnicodeEncoding.GetByteCount]Expected empty fallback buffer at end");
 628
 629            // Don't remember fallbackBuffer.encoder for counting
 0630            return byteCount;
 631        }
 632
 633        internal sealed override unsafe int GetBytes(
 634            char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS? encoder)
 635        {
 636            Debug.Assert(chars is not null, "[UnicodeEncoding.GetBytes]chars!=null");
 0637            Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetBytes]byteCount >=0");
 0638            Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetBytes]charCount >=0");
 0639            Debug.Assert(bytes is not null, "[UnicodeEncoding.GetBytes]bytes!=null");
 640
 0641            char charLeftOver = (char)0;
 642            char ch;
 0643            bool wasHereBefore = false;
 644
 0645            byte* byteEnd = bytes + byteCount;
 0646            char* charEnd = chars + charCount;
 0647            byte* byteStart = bytes;
 0648            char* charStart = chars;
 649
 650            // For fallback we may need a fallback buffer
 0651            EncoderFallbackBuffer? fallbackBuffer = null;
 652            char* charsForFallback;
 653
 654            // Get our encoder, but don't clear it yet.
 0655            if (encoder is not null)
 656            {
 0657                charLeftOver = encoder._charLeftOver;
 658
 659                // We mustn't have left over fallback data when counting
 0660                if (encoder.InternalHasFallbackBuffer)
 661                {
 662                    // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
 0663                    fallbackBuffer = encoder.FallbackBuffer;
 0664                    if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow)
 0665                        throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, en
 666
 667                    // Set our internal fallback interesting things.
 0668                    fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
 669                }
 670            }
 671
 672        TryAgain:
 0673            while (((ch = (fallbackBuffer is null) ?
 0674                        (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) ||
 0675                    chars < charEnd)
 676            {
 677                // First unwind any fallback
 0678                if (ch == 0)
 679                {
 680                    // No fallback, maybe we can do it fast
 681#if FASTLOOP
 682                    // If endianness is backwards then each pair of bytes would be backwards.
 0683                    if ((bigEndian ^ BitConverter.IsLittleEndian) &&
 0684#if TARGET_64BIT
 0685                        (unchecked((long)chars) & 7) == 0 &&
 0686#else
 0687                        (unchecked((int)chars) & 3) == 0 &&
 0688#endif
 0689                        charLeftOver == 0)
 690                    {
 691                        // Need -1 to check 2 at a time.  If we have an even #, longChars will go
 692                        // from longEnd - 1/2 long to longEnd + 1/2 long.  If we're odd, longChars
 693                        // will go from longEnd - 1 long to longEnd. (Might not get to use this)
 694                        // We can only go iCount units (limited by shorter of char or byte buffers.
 0695                        ulong* longEnd = (ulong*)(chars - 3 +
 0696                                                  (((byteEnd - bytes) >> 1 < charEnd - chars) ?
 0697                                                    (byteEnd - bytes) >> 1 : charEnd - chars));
 698
 699                        // Need new char* so we can check 4 at a time
 0700                        ulong* longChars = (ulong*)chars;
 0701                        ulong* longBytes = (ulong*)bytes;
 702
 0703                        while (longChars < longEnd)
 704                        {
 705                            // See if we potentially have surrogates (0x8000 bit set)
 706                            // (We're either big endian on a big endian machine or little endian on
 707                            // a little endian machine so that'll work)
 0708                            if ((0x8000800080008000 & *longChars) != 0)
 709                            {
 710                                // See if any of these are high or low surrogates (0xd800 - 0xdfff).  If the high
 711                                // 5 bits looks like 11011, then its a high or low surrogate.
 712                                // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
 713                                // Note that we expect BMP characters to be more common than surrogates
 714                                // & each char with 11111... then ^ with 11011.  Zeroes then indicate surrogates
 0715                                ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
 716
 717                                // Check each of the 4 chars.  0 for those 16 bits means it was a surrogate
 718                                // but no clue if they're high or low.
 719                                // If each of the 4 characters are non-zero, then none are surrogates.
 0720                                if ((uTemp & 0xFFFF000000000000) == 0 ||
 0721                                    (uTemp & 0x0000FFFF00000000) == 0 ||
 0722                                    (uTemp & 0x00000000FFFF0000) == 0 ||
 0723                                    (uTemp & 0x000000000000FFFF) == 0)
 724                                {
 725                                    // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
 726                                    // or if there's 1 or 4 surrogates
 727
 728                                    // If they happen to be high/low/high/low, we may as well continue.  Check the next
 729                                    // bit to see if its set (low) or not (high) in the right pattern
 0730                                    if ((0xfc00fc00fc00fc00 & *longChars) !=
 0731                                            (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00
 732                                    {
 733                                        // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
 734                                        // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
 735
 736                                        // Drop out to the slow loop to resolve the surrogates
 737                                        break;
 738                                    }
 739                                    // else they are all surrogates in High/Low/High/Low order, so we can use them.
 740                                }
 741                                // else none are surrogates, so we can use them.
 742                            }
 743                            // else all < 0x8000 so we can use them
 744
 745                            // We can use these 4 chars.
 0746                            Unsafe.WriteUnaligned(longBytes, *longChars);
 0747                            longChars++;
 0748                            longBytes++;
 749                        }
 750
 0751                        chars = (char*)longChars;
 0752                        bytes = (byte*)longBytes;
 753
 0754                        if (chars >= charEnd)
 755                            break;
 756                    }
 757#endif // FASTLOOP
 758
 759                    // No fallback, just get next char
 0760                    ch = *chars;
 0761                    chars++;
 762                }
 763
 764                // Check for high or low surrogates
 0765                if (ch >= 0xd800 && ch <= 0xdfff)
 766                {
 767                    // Was it a high surrogate?
 0768                    if (ch <= 0xdbff)
 769                    {
 770                        // Its a high surrogate, see if we already had a high surrogate
 0771                        if (charLeftOver > 0)
 772                        {
 773                            // Unwind the current character, this should be safe because we
 774                            // don't have leftover data in the fallback, so chars must have
 775                            // advanced already.
 0776                            Debug.Assert(chars > charStart,
 0777                                "[UnicodeEncoding.GetBytes]Expected chars to have advanced in unexpected high surrogate"
 0778                            chars--;
 779
 780                            // Fallback the previous surrogate
 781                            // Might need to create our fallback buffer
 0782                            if (fallbackBuffer is null)
 783                            {
 0784                                fallbackBuffer = encoder is null ?
 0785                                    this.encoderFallback.CreateFallbackBuffer() :
 0786                                    encoder.FallbackBuffer;
 787
 788                                // Set our internal fallback interesting things.
 0789                                fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
 790                            }
 791
 0792                            charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registere
 0793                            fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0794                            chars = charsForFallback;
 795
 0796                            charLeftOver = (char)0;
 0797                            continue;
 798                        }
 799
 800                        // Remember this high surrogate
 0801                        charLeftOver = ch;
 0802                        continue;
 803                    }
 804
 805                    // Its a low surrogate
 0806                    if (charLeftOver == 0)
 807                    {
 808                        // We'll fall back this one
 809                        // Might need to create our fallback buffer
 0810                        if (fallbackBuffer is null)
 811                        {
 0812                            fallbackBuffer = encoder is null ?
 0813                                this.encoderFallback.CreateFallbackBuffer() :
 0814                                encoder.FallbackBuffer;
 815
 816                            // Set our internal fallback interesting things.
 0817                            fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
 818                        }
 819
 0820                        charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0821                        fallbackBuffer.InternalFallback(ch, ref charsForFallback);
 0822                        chars = charsForFallback;
 0823                        continue;
 824                    }
 825
 826                    // Valid surrogate pair, add our charLeftOver
 0827                    if (bytes + 3 >= byteEnd)
 828                    {
 829                        // Not enough room to add this surrogate pair
 0830                        if (fallbackBuffer is not null && fallbackBuffer.bFallingBack)
 831                        {
 832                            // These must have both been from the fallbacks.
 833                            // Both of these MUST have been from a fallback because if the 1st wasn't
 834                            // from a fallback, then a high surrogate followed by an illegal char
 835                            // would've caused the high surrogate to fall back.  If a high surrogate
 836                            // fell back, then it was consumed and both chars came from the fallback.
 0837                            fallbackBuffer.MovePrevious();                     // Didn't use either fallback surrogate
 0838                            fallbackBuffer.MovePrevious();
 839                        }
 840                        else
 841                        {
 842                            // If we don't have enough room, then either we should've advanced a while
 843                            // or we should have bytes==byteStart and throw below
 0844                            Debug.Assert(chars > charStart + 1 || bytes == byteStart,
 0845                                "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair");
 0846                            chars -= 2;                                        // Didn't use either surrogate
 847                        }
 0848                        ThrowBytesOverflow(encoder, bytes == byteStart);    // Throw maybe (if no bytes written)
 0849                        charLeftOver = (char)0;                             // we'll retry it later
 0850                        break;                                               // Didn't throw, but stop 'til next time.
 851                    }
 852
 0853                    if (bigEndian)
 854                    {
 0855                        *(bytes++) = (byte)(charLeftOver >> 8);
 0856                        *(bytes++) = (byte)charLeftOver;
 857                    }
 858                    else
 859                    {
 0860                        *(bytes++) = (byte)charLeftOver;
 0861                        *(bytes++) = (byte)(charLeftOver >> 8);
 862                    }
 863
 0864                    charLeftOver = (char)0;
 865                }
 0866                else if (charLeftOver > 0)
 867                {
 868                    // Expected a low surrogate, but this char is normal
 869
 870                    // Rewind the current character, fallback previous character.
 871                    // this should be safe because we don't have leftover data in the
 872                    // fallback, so chars must have advanced already.
 0873                    Debug.Assert(chars > charStart,
 0874                        "[UnicodeEncoding.GetBytes]Expected chars to have advanced after expecting low surrogate");
 0875                    chars--;
 876
 877                    // fallback previous chars
 878                    // Might need to create our fallback buffer
 0879                    if (fallbackBuffer is null)
 880                    {
 0881                        fallbackBuffer = encoder is null ?
 0882                            this.encoderFallback.CreateFallbackBuffer() :
 0883                            encoder.FallbackBuffer;
 884
 885                        // Set our internal fallback interesting things.
 0886                        fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
 887                    }
 888
 0889                    charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0890                    fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0891                    chars = charsForFallback;
 892
 893                    // Ignore charLeftOver or throw
 0894                    charLeftOver = (char)0;
 0895                    continue;
 896                }
 897
 898                // Ok, we have a char to add
 0899                if (bytes + 1 >= byteEnd)
 900                {
 901                    // Couldn't add this char
 0902                    if (fallbackBuffer is not null && fallbackBuffer.bFallingBack)
 0903                        fallbackBuffer.MovePrevious();                     // Not using this fallback char
 904                    else
 905                    {
 906                        // Lonely charLeftOver (from previous call) would've been caught up above,
 907                        // so this must be a case where we've already read an input char.
 0908                        Debug.Assert(chars > charStart,
 0909                            "[UnicodeEncoding.GetBytes]Expected chars to have advanced for failed fallback");
 0910                        chars--;                                         // Not using this char
 911                    }
 0912                    ThrowBytesOverflow(encoder, bytes == byteStart);    // Throw maybe (if no bytes written)
 0913                    break;                                               // didn't throw, just stop
 914                }
 915
 0916                if (bigEndian)
 917                {
 0918                    *(bytes++) = (byte)(ch >> 8);
 0919                    *(bytes++) = (byte)ch;
 920                }
 921                else
 922                {
 0923                    *(bytes++) = (byte)ch;
 0924                    *(bytes++) = (byte)(ch >> 8);
 925                }
 926            }
 927
 928            // Don't allocate space for left over char
 0929            if (charLeftOver > 0)
 930            {
 931                // If we aren't flushing we need to fall this back
 0932                if (encoder is null || encoder.MustFlush)
 933                {
 0934                    if (wasHereBefore)
 935                    {
 936                        // Throw it, using our complete character
 0937                        throw new ArgumentException(
 0938                            SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
 939                    }
 940                    else
 941                    {
 942                        // If we have to flush, stick it in fallback and try again
 943                        // Might need to create our fallback buffer
 0944                        if (fallbackBuffer is null)
 945                        {
 0946                            fallbackBuffer = encoder is null ?
 0947                                this.encoderFallback.CreateFallbackBuffer() :
 0948                                encoder.FallbackBuffer;
 949
 950                            // Set our internal fallback interesting things.
 0951                            fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
 952                        }
 953
 954                        // If we're not flushing, that'll remember the left over character.
 0955                        charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 0956                        fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
 0957                        chars = charsForFallback;
 958
 0959                        charLeftOver = (char)0;
 0960                        wasHereBefore = true;
 0961                        goto TryAgain;
 962                    }
 963                }
 964            }
 965
 966            // Not flushing, remember it in the encoder
 0967            if (encoder is not null)
 968            {
 0969                encoder._charLeftOver = charLeftOver;
 0970                encoder._charsUsed = (int)(chars - charStart);
 971            }
 972
 973            // Remember charLeftOver if we must, or clear it if we're flushing
 974            // (charLeftOver should be 0 if we're flushing)
 0975            Debug.Assert((encoder is not null && !encoder.MustFlush) || charLeftOver == (char)0,
 0976                "[UnicodeEncoding.GetBytes] Expected no left over characters if flushing");
 977
 0978            Debug.Assert(fallbackBuffer is null || fallbackBuffer.Remaining == 0 ||
 0979                encoder is null || !encoder._throwOnOverflow,
 0980                "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting");
 981
 0982            return (int)(bytes - byteStart);
 983        }
 984
 985        internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS? baseDecoder)
 986        {
 987            Debug.Assert(bytes is not null, "[UnicodeEncoding.GetCharCount]bytes!=null");
 0988            Debug.Assert(count >= 0, "[UnicodeEncoding.GetCharCount]count >=0");
 989
 0990            Decoder? decoder = (Decoder?)baseDecoder;
 991
 0992            byte* byteEnd = bytes + count;
 0993            byte* byteStart = bytes;
 994
 995            // Need last vars
 0996            int lastByte = -1;
 0997            char lastChar = (char)0;
 998
 999            // Start by assuming same # of chars as bytes
 01000            int charCount = count >> 1;
 1001
 1002            // For fallback we may need a fallback buffer
 01003            DecoderFallbackBuffer? fallbackBuffer = null;
 1004
 01005            if (decoder is not null)
 1006            {
 01007                lastByte = decoder.lastByte;
 01008                lastChar = decoder.lastChar;
 1009
 1010                // Assume extra char if last char was around
 01011                if (lastChar > 0)
 01012                    charCount++;
 1013
 1014                // Assume extra char if extra last byte makes up odd # of input bytes
 01015                if (lastByte >= 0 && (count & 1) == 1)
 1016                {
 01017                    charCount++;
 1018                }
 1019
 1020                // Shouldn't have anything in fallback buffer for GetCharCount
 1021                // (don't have to check _throwOnOverflow for count)
 01022                Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
 01023                    "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at start");
 1024            }
 1025
 01026            while (bytes < byteEnd)
 1027            {
 1028                // If we're aligned then maybe we can do it fast
 1029                // That'll hurt if we're unaligned because we'll always test but never be aligned
 1030#if FASTLOOP
 01031                if ((bigEndian ^ BitConverter.IsLittleEndian) &&
 01032#if TARGET_64BIT
 01033                    (unchecked((long)bytes) & 7) == 0 &&
 01034#else
 01035                    (unchecked((int)bytes) & 3) == 0 &&
 01036#endif // TARGET_64BIT
 01037                    lastByte == -1 && lastChar == 0)
 1038                {
 1039                    // Need -1 to check 2 at a time.  If we have an even #, longBytes will go
 1040                    // from longEnd - 1/2 long to longEnd + 1/2 long.  If we're odd, longBytes
 1041                    // will go from longEnd - 1 long to longEnd. (Might not get to use this)
 01042                    ulong* longEnd = (ulong*)(byteEnd - 7);
 1043
 1044                    // Need new char* so we can check 4 at a time
 01045                    ulong* longBytes = (ulong*)bytes;
 1046
 01047                    while (longBytes < longEnd)
 1048                    {
 1049                        // See if we potentially have surrogates (0x8000 bit set)
 1050                        // (We're either big endian on a big endian machine or little endian on
 1051                        // a little endian machine so that'll work)
 01052                        if ((0x8000800080008000 & *longBytes) != 0)
 1053                        {
 1054                            // See if any of these are high or low surrogates (0xd800 - 0xdfff).  If the high
 1055                            // 5 bits looks like 11011, then its a high or low surrogate.
 1056                            // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
 1057                            // Note that we expect BMP characters to be more common than surrogates
 1058                            // & each char with 11111... then ^ with 11011.  Zeroes then indicate surrogates
 01059                            ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
 1060
 1061                            // Check each of the 4 chars.  0 for those 16 bits means it was a surrogate
 1062                            // but no clue if they're high or low.
 1063                            // If each of the 4 characters are non-zero, then none are surrogates.
 01064                            if ((uTemp & 0xFFFF000000000000) == 0 ||
 01065                                (uTemp & 0x0000FFFF00000000) == 0 ||
 01066                                (uTemp & 0x00000000FFFF0000) == 0 ||
 01067                                (uTemp & 0x000000000000FFFF) == 0)
 1068                            {
 1069                                // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
 1070                                // or if there's 1 or 4 surrogates
 1071
 1072                                // If they happen to be high/low/high/low, we may as well continue.  Check the next
 1073                                // bit to see if its set (low) or not (high) in the right pattern
 01074                                if ((0xfc00fc00fc00fc00 & *longBytes) !=
 01075                                        (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800
 1076                                {
 1077                                    // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
 1078                                    // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
 1079
 1080                                    // Drop out to the slow loop to resolve the surrogates
 1081                                    break;
 1082                                }
 1083                                // else they are all surrogates in High/Low/High/Low order, so we can use them.
 1084                            }
 1085                            // else none are surrogates, so we can use them.
 1086                        }
 1087                        // else all < 0x8000 so we can use them
 1088
 1089                        // We can use these 4 chars.
 01090                        longBytes++;
 1091                    }
 1092
 01093                    bytes = (byte*)longBytes;
 1094
 01095                    if (bytes >= byteEnd)
 1096                        break;
 1097                }
 1098#endif // FASTLOOP
 1099
 1100                // Get 1st byte
 01101                if (lastByte < 0)
 1102                {
 01103                    lastByte = *bytes++;
 01104                    if (bytes >= byteEnd) break;
 1105                }
 1106
 1107                // Get full char
 1108                char ch;
 01109                if (bigEndian)
 1110                {
 01111                    ch = (char)(lastByte << 8 | *(bytes++));
 1112                }
 1113                else
 1114                {
 01115                    ch = (char)(*(bytes++) << 8 | lastByte);
 1116                }
 01117                lastByte = -1;
 1118
 1119                // See if the char's valid
 01120                if (ch >= 0xd800 && ch <= 0xdfff)
 1121                {
 1122                    // Was it a high surrogate?
 01123                    if (ch <= 0xdbff)
 1124                    {
 1125                        // Its a high surrogate, if we had one then do fallback for previous one
 01126                        if (lastChar > 0)
 1127                        {
 1128                            // Ignore previous bad high surrogate
 01129                            charCount--;
 1130
 1131                            // Get fallback for previous high surrogate
 1132                            // Note we have to reconstruct bytes because some may have been in decoder
 01133                            byte[]? byteBuffer = null;
 01134                            if (bigEndian)
 1135                            {
 01136                                byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1137                            }
 1138                            else
 1139                            {
 01140                                byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1141                            }
 1142
 01143                            if (fallbackBuffer is null)
 1144                            {
 01145                                fallbackBuffer = decoder is null ?
 01146                                    this.decoderFallback.CreateFallbackBuffer() :
 01147                                    decoder.FallbackBuffer;
 1148
 1149                                // Set our internal fallback interesting things.
 01150                                fallbackBuffer.InternalInitialize(byteStart, null);
 1151                            }
 1152
 1153                            // Get fallback.
 01154                            charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
 1155                        }
 1156
 1157                        // Ignore the last one which fell back already,
 1158                        // and remember the new high surrogate
 01159                        lastChar = ch;
 01160                        continue;
 1161                    }
 1162
 1163                    // Its a low surrogate
 01164                    if (lastChar == 0)
 1165                    {
 1166                        // Expected a previous high surrogate
 01167                        charCount--;
 1168
 1169                        // Get fallback for this low surrogate
 1170                        // Note we have to reconstruct bytes because some may have been in decoder
 01171                        byte[]? byteBuffer = null;
 01172                        if (bigEndian)
 1173                        {
 01174                            byteBuffer = [unchecked((byte)(ch >> 8)), unchecked((byte)ch)];
 1175                        }
 1176                        else
 1177                        {
 01178                            byteBuffer = [unchecked((byte)ch), unchecked((byte)(ch >> 8))];
 1179                        }
 1180
 01181                        if (fallbackBuffer is null)
 1182                        {
 01183                            fallbackBuffer = decoder is null ?
 01184                                this.decoderFallback.CreateFallbackBuffer() :
 01185                                decoder.FallbackBuffer;
 1186
 1187                            // Set our internal fallback interesting things.
 01188                            fallbackBuffer.InternalInitialize(byteStart, null);
 1189                        }
 1190
 01191                        charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
 1192
 1193                        // Ignore this one (we already did its fallback)
 01194                        continue;
 1195                    }
 1196
 1197                    // Valid surrogate pair, already counted.
 01198                    lastChar = (char)0;
 1199                }
 01200                else if (lastChar > 0)
 1201                {
 1202                    // Had a high surrogate, expected a low surrogate
 1203                    // Un-count the last high surrogate
 01204                    charCount--;
 1205
 1206                    // fall back the high surrogate.
 01207                    byte[]? byteBuffer = null;
 01208                    if (bigEndian)
 1209                    {
 01210                        byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1211                    }
 1212                    else
 1213                    {
 01214                        byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1215                    }
 1216
 01217                    if (fallbackBuffer is null)
 1218                    {
 01219                        fallbackBuffer = decoder is null ?
 01220                            this.decoderFallback.CreateFallbackBuffer() :
 01221                            decoder.FallbackBuffer;
 1222
 1223                        // Set our internal fallback interesting things.
 01224                        fallbackBuffer.InternalInitialize(byteStart, null);
 1225                    }
 1226
 1227                    // Already subtracted high surrogate
 01228                    charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
 1229
 1230                    // Not left over now, clear previous high surrogate and continue to add current char
 01231                    lastChar = (char)0;
 1232                }
 1233
 1234                // Valid char, already counted
 1235            }
 1236
 1237            // Extra space if we can't use decoder
 01238            if (decoder is null || decoder.MustFlush)
 1239            {
 01240                if (lastChar > 0)
 1241                {
 1242                    // No hanging high surrogates allowed, do fallback and remove count for it
 01243                    charCount--;
 01244                    byte[]? byteBuffer = null;
 01245                    if (bigEndian)
 1246                    {
 01247                        byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1248                    }
 1249                    else
 1250                    {
 01251                        byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1252                    }
 1253
 01254                    if (fallbackBuffer is null)
 1255                    {
 01256                        fallbackBuffer = decoder is null ?
 01257                            this.decoderFallback.CreateFallbackBuffer() :
 01258                            decoder.FallbackBuffer;
 1259
 1260                        // Set our internal fallback interesting things.
 01261                        fallbackBuffer.InternalInitialize(byteStart, null);
 1262                    }
 1263
 01264                    charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
 1265
 01266                    lastChar = (char)0;
 1267                }
 1268
 01269                if (lastByte >= 0)
 1270                {
 01271                    if (fallbackBuffer is null)
 1272                    {
 01273                        fallbackBuffer = decoder is null ?
 01274                            this.decoderFallback.CreateFallbackBuffer() :
 01275                            decoder.FallbackBuffer;
 1276
 1277                        // Set our internal fallback interesting things.
 01278                        fallbackBuffer.InternalInitialize(byteStart, null);
 1279                    }
 1280
 1281                    // No hanging odd bytes allowed if must flush
 01282                    charCount += fallbackBuffer.InternalFallback([unchecked((byte)lastByte)], bytes);
 01283                    lastByte = -1;
 1284                }
 1285            }
 1286
 1287            // If we had a high surrogate left over, we can't count it
 01288            if (lastChar > 0)
 01289                charCount--;
 1290
 1291            // Shouldn't have anything in fallback buffer for GetCharCount
 1292            // (don't have to check _throwOnOverflow for count)
 01293            Debug.Assert(fallbackBuffer is null || fallbackBuffer.Remaining == 0,
 01294                "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at end");
 1295
 01296            return charCount;
 1297        }
 1298
 1299        internal sealed override unsafe int GetChars(
 1300            byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS? baseDecoder)
 1301        {
 1302            Debug.Assert(chars is not null, "[UnicodeEncoding.GetChars]chars!=null");
 01303            Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetChars]byteCount >=0");
 01304            Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetChars]charCount >=0");
 01305            Debug.Assert(bytes is not null, "[UnicodeEncoding.GetChars]bytes!=null");
 1306
 01307            Decoder? decoder = (Decoder?)baseDecoder;
 1308
 1309            // Need last vars
 01310            int lastByte = -1;
 01311            char lastChar = (char)0;
 1312
 1313            // Get our decoder (but don't clear it yet)
 01314            if (decoder is not null)
 1315            {
 01316                lastByte = decoder.lastByte;
 01317                lastChar = decoder.lastChar;
 1318
 1319                // Shouldn't have anything in fallback buffer for GetChars
 1320                // (don't have to check _throwOnOverflow for chars)
 01321                Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
 01322                    "[UnicodeEncoding.GetChars]Expected empty fallback buffer at start");
 1323            }
 1324
 1325            // For fallback we may need a fallback buffer
 01326            DecoderFallbackBuffer? fallbackBuffer = null;
 1327            char* charsForFallback;
 1328
 01329            byte* byteEnd = bytes + byteCount;
 01330            char* charEnd = chars + charCount;
 01331            byte* byteStart = bytes;
 01332            char* charStart = chars;
 1333
 01334            while (bytes < byteEnd)
 1335            {
 1336                // If we're aligned then maybe we can do it fast
 1337                // That'll hurt if we're unaligned because we'll always test but never be aligned
 1338#if FASTLOOP
 01339                if ((bigEndian ^ BitConverter.IsLittleEndian) &&
 01340#if TARGET_64BIT
 01341                    (unchecked((long)chars) & 7) == 0 &&
 01342#else
 01343                    (unchecked((int)chars) & 3) == 0 &&
 01344#endif
 01345                    lastByte == -1 && lastChar == 0)
 1346                {
 1347                    // Need -1 to check 2 at a time.  If we have an even #, longChars will go
 1348                    // from longEnd - 1/2 long to longEnd + 1/2 long.  If we're odd, longChars
 1349                    // will go from longEnd - 1 long to longEnd. (Might not get to use this)
 1350                    // We can only go iCount units (limited by shorter of char or byte buffers.
 01351                    ulong* longEnd = (ulong*)(bytes - 7 +
 01352                                                (((byteEnd - bytes) >> 1 < charEnd - chars) ?
 01353                                                  (byteEnd - bytes) : (charEnd - chars) << 1));
 1354
 1355                    // Need new char* so we can check 4 at a time
 01356                    ulong* longBytes = (ulong*)bytes;
 01357                    ulong* longChars = (ulong*)chars;
 1358
 01359                    while (longBytes < longEnd)
 1360                    {
 1361                        // See if we potentially have surrogates (0x8000 bit set)
 1362                        // (We're either big endian on a big endian machine or little endian on
 1363                        // a little endian machine so that'll work)
 01364                        if ((0x8000800080008000 & *longBytes) != 0)
 1365                        {
 1366                            // See if any of these are high or low surrogates (0xd800 - 0xdfff).  If the high
 1367                            // 5 bits looks like 11011, then its a high or low surrogate.
 1368                            // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
 1369                            // Note that we expect BMP characters to be more common than surrogates
 1370                            // & each char with 11111... then ^ with 11011.  Zeroes then indicate surrogates
 01371                            ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
 1372
 1373                            // Check each of the 4 chars.  0 for those 16 bits means it was a surrogate
 1374                            // but no clue if they're high or low.
 1375                            // If each of the 4 characters are non-zero, then none are surrogates.
 01376                            if ((uTemp & 0xFFFF000000000000) == 0 ||
 01377                                (uTemp & 0x0000FFFF00000000) == 0 ||
 01378                                (uTemp & 0x00000000FFFF0000) == 0 ||
 01379                                (uTemp & 0x000000000000FFFF) == 0)
 1380                            {
 1381                                // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
 1382                                // or if there's 1 or 4 surrogates
 1383
 1384                                // If they happen to be high/low/high/low, we may as well continue.  Check the next
 1385                                // bit to see if its set (low) or not (high) in the right pattern
 01386                                if ((0xfc00fc00fc00fc00 & *longBytes) !=
 01387                                        (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800
 1388                                {
 1389                                    // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
 1390                                    // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
 1391
 1392                                    // Drop out to the slow loop to resolve the surrogates
 1393                                    break;
 1394                                }
 1395                                // else they are all surrogates in High/Low/High/Low order, so we can use them.
 1396                            }
 1397                            // else none are surrogates, so we can use them.
 1398                        }
 1399                        // else all < 0x8000 so we can use them
 1400
 1401                        // We can use these 4 chars.
 01402                        Unsafe.WriteUnaligned(longChars, *longBytes);
 01403                        longBytes++;
 01404                        longChars++;
 1405                    }
 1406
 01407                    chars = (char*)longChars;
 01408                    bytes = (byte*)longBytes;
 1409
 01410                    if (bytes >= byteEnd)
 1411                        break;
 1412                }
 1413#endif // FASTLOOP
 1414
 1415                // Get 1st byte
 01416                if (lastByte < 0)
 1417                {
 01418                    lastByte = *bytes++;
 01419                    continue;
 1420                }
 1421
 1422                // Get full char
 1423                char ch;
 01424                if (bigEndian)
 1425                {
 01426                    ch = (char)(lastByte << 8 | *(bytes++));
 1427                }
 1428                else
 1429                {
 01430                    ch = (char)(*(bytes++) << 8 | lastByte);
 1431                }
 01432                lastByte = -1;
 1433
 1434                // See if the char's valid
 01435                if (ch >= 0xd800 && ch <= 0xdfff)
 1436                {
 1437                    // Was it a high surrogate?
 01438                    if (ch <= 0xdbff)
 1439                    {
 1440                        // Its a high surrogate, if we had one then do fallback for previous one
 01441                        if (lastChar > 0)
 1442                        {
 1443                            // Get fallback for previous high surrogate
 1444                            // Note we have to reconstruct bytes because some may have been in decoder
 01445                            byte[]? byteBuffer = null;
 01446                            if (bigEndian)
 1447                            {
 01448                                byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1449                            }
 1450                            else
 1451                            {
 01452                                byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1453                            }
 1454
 01455                            if (fallbackBuffer is null)
 1456                            {
 01457                                fallbackBuffer = decoder is null ?
 01458                                    this.decoderFallback.CreateFallbackBuffer() :
 01459                                    decoder.FallbackBuffer;
 1460
 1461                                // Set our internal fallback interesting things.
 01462                                fallbackBuffer.InternalInitialize(byteStart, charEnd);
 1463                            }
 1464
 01465                            charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registere
 01466                            bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallbac
 01467                            chars = charsForFallback;
 1468
 01469                            if (!fallbackResult)
 1470                            {
 1471                                // couldn't fall back lonely surrogate
 1472                                // We either advanced bytes or chars should == charStart and throw below
 01473                                Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01474                                    "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (bad surroga
 01475                                bytes -= 2;                                       // didn't use these 2 bytes
 01476                                fallbackBuffer.InternalReset();
 01477                                ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 01478                                break;                                           // couldn't fallback but didn't throw
 1479                            }
 1480                        }
 1481
 1482                        // Ignore the previous high surrogate which fell back already,
 1483                        // yet remember the current high surrogate for next time.
 01484                        lastChar = ch;
 01485                        continue;
 1486                    }
 1487
 1488                    // Its a low surrogate
 01489                    if (lastChar == 0)
 1490                    {
 1491                        // Expected a previous high surrogate
 1492                        // Get fallback for this low surrogate
 1493                        // Note we have to reconstruct bytes because some may have been in decoder
 01494                        byte[]? byteBuffer = null;
 01495                        if (bigEndian)
 1496                        {
 01497                            byteBuffer = [unchecked((byte)(ch >> 8)), unchecked((byte)ch)];
 1498                        }
 1499                        else
 1500                        {
 01501                            byteBuffer = [unchecked((byte)ch), unchecked((byte)(ch >> 8))];
 1502                        }
 1503
 01504                        if (fallbackBuffer is null)
 1505                        {
 01506                            fallbackBuffer = decoder is null ?
 01507                                this.decoderFallback.CreateFallbackBuffer() :
 01508                                decoder.FallbackBuffer;
 1509
 1510                            // Set our internal fallback interesting things.
 01511                            fallbackBuffer.InternalInitialize(byteStart, charEnd);
 1512                        }
 1513
 01514                        charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 01515                        bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
 01516                        chars = charsForFallback;
 1517
 01518                        if (!fallbackResult)
 1519                        {
 1520                            // couldn't fall back lonely surrogate
 1521                            // We either advanced bytes or chars should == charStart and throw below
 01522                            Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01523                                "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (lonely surrogat
 01524                            bytes -= 2;                                       // didn't use these 2 bytes
 01525                            fallbackBuffer.InternalReset();
 01526                            ThrowCharsOverflow(decoder, chars == charStart);  // Might throw, if no chars output
 01527                            break;                                            // couldn't fallback but didn't throw
 1528                        }
 1529
 1530                        // Didn't throw, ignore this one (we already did its fallback)
 1531                        continue;
 1532                    }
 1533
 1534                    // Valid surrogate pair, add our lastChar (will need 2 chars)
 01535                    if (charEnd - chars < 2)
 1536                    {
 1537                        // couldn't find room for this surrogate pair
 1538                        // We either advanced bytes or chars should == charStart and throw below
 01539                        Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01540                            "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (surrogate pair)");
 01541                        bytes -= 2;                                       // didn't use these 2 bytes
 01542                        ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 1543                        // Leave lastChar for next call to Convert()
 01544                        break;                                           // couldn't fallback but didn't throw
 1545                    }
 1546
 01547                    *chars++ = lastChar;
 01548                    lastChar = (char)0;
 1549                }
 01550                else if (lastChar > 0)
 1551                {
 1552                    // Had a high surrogate, expected a low surrogate, fall back the high surrogate.
 01553                    byte[]? byteBuffer = null;
 01554                    if (bigEndian)
 1555                    {
 01556                        byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1557                    }
 1558                    else
 1559                    {
 01560                        byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1561                    }
 1562
 01563                    if (fallbackBuffer is null)
 1564                    {
 01565                        fallbackBuffer = decoder is null ?
 01566                            this.decoderFallback.CreateFallbackBuffer() :
 01567                            decoder.FallbackBuffer;
 1568
 1569                        // Set our internal fallback interesting things.
 01570                        fallbackBuffer.InternalInitialize(byteStart, charEnd);
 1571                    }
 1572
 01573                    charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 01574                    bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
 01575                    chars = charsForFallback;
 1576
 01577                    if (!fallbackResult)
 1578                    {
 1579                        // couldn't fall back high surrogate, or char that would be next
 1580                        // We either advanced bytes or chars should == charStart and throw below
 01581                        Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01582                            "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (no low surrogate)")
 01583                        bytes -= 2;                                       // didn't use these 2 bytes
 01584                        fallbackBuffer.InternalReset();
 01585                        ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 01586                        break;                                           // couldn't fallback but didn't throw
 1587                    }
 1588
 1589                    // Not left over now, clear previous high surrogate and continue to add current char
 01590                    lastChar = (char)0;
 1591                }
 1592
 1593                // Valid char, room for it?
 01594                if (chars >= charEnd)
 1595                {
 1596                    // 2 bytes couldn't fall back
 1597                    // We either advanced bytes or chars should == charStart and throw below
 01598                    Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01599                        "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (normal)");
 01600                    bytes -= 2;                                       // didn't use these bytes
 01601                    ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 01602                    break;                                           // couldn't fallback but didn't throw
 1603                }
 1604
 1605                // add it
 01606                *chars++ = ch;
 1607            }
 1608
 1609            // Remember our decoder if we must
 01610            if (decoder is null || decoder.MustFlush)
 1611            {
 01612                if (lastChar > 0)
 1613                {
 1614                    // No hanging high surrogates allowed, do fallback and remove count for it
 01615                    byte[]? byteBuffer = null;
 01616                    if (bigEndian)
 1617                    {
 01618                        byteBuffer = [unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar)];
 1619                    }
 1620                    else
 1621                    {
 01622                        byteBuffer = [unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8))];
 1623                    }
 1624
 01625                    if (fallbackBuffer is null)
 1626                    {
 01627                        fallbackBuffer = decoder is null ?
 01628                            this.decoderFallback.CreateFallbackBuffer() :
 01629                            decoder.FallbackBuffer;
 1630
 1631                        // Set our internal fallback interesting things.
 01632                        fallbackBuffer.InternalInitialize(byteStart, charEnd);
 1633                    }
 1634
 01635                    charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 01636                    bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
 01637                    chars = charsForFallback;
 1638
 01639                    if (!fallbackResult)
 1640                    {
 1641                        // 2 bytes couldn't fall back
 1642                        // We either advanced bytes or chars should == charStart and throw below
 01643                        Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
 01644                            "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (decoder)");
 01645                        bytes -= 2;                                       // didn't use these bytes
 01646                        if (lastByte >= 0)
 01647                            bytes--;                                    // had an extra last byte hanging around
 01648                        fallbackBuffer.InternalReset();
 01649                        ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 1650                        // We'll remember these in our decoder though
 01651                        bytes += 2;
 01652                        if (lastByte >= 0)
 01653                            bytes++;
 01654                        goto End;
 1655                    }
 1656
 1657                    // done with this one
 01658                    lastChar = (char)0;
 1659                }
 1660
 01661                if (lastByte >= 0)
 1662                {
 01663                    if (fallbackBuffer is null)
 1664                    {
 01665                        fallbackBuffer = decoder is null ?
 01666                            this.decoderFallback.CreateFallbackBuffer() :
 01667                            decoder.FallbackBuffer;
 1668
 1669                        // Set our internal fallback interesting things.
 01670                        fallbackBuffer.InternalInitialize(byteStart, charEnd);
 1671                    }
 1672
 1673                    // No hanging odd bytes allowed if must flush
 01674                    charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
 01675                    bool fallbackResult = fallbackBuffer.InternalFallback([unchecked((byte)lastByte)], bytes, ref charsF
 01676                    chars = charsForFallback;
 1677
 01678                    if (!fallbackResult)
 1679                    {
 1680                        // odd byte couldn't fall back
 01681                        bytes--;                                        // didn't use this byte
 01682                        fallbackBuffer.InternalReset();
 01683                        ThrowCharsOverflow(decoder, chars == charStart); // Might throw, if no chars output
 1684                        // didn't throw, but we'll remember it in the decoder
 01685                        bytes++;
 01686                        goto End;
 1687                    }
 1688
 1689                    // Didn't fail, clear buffer
 01690                    lastByte = -1;
 1691                }
 1692            }
 1693
 1694        End:
 1695
 1696            // Remember our decoder if we must
 01697            if (decoder is not null)
 1698            {
 01699                Debug.Assert(!decoder.MustFlush || ((lastChar == (char)0) && (lastByte == -1)),
 01700                    "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing");
 1701
 01702                decoder._bytesUsed = (int)(bytes - byteStart);
 01703                decoder.lastChar = lastChar;
 01704                decoder.lastByte = lastByte;
 1705            }
 1706
 1707            // Shouldn't have anything in fallback buffer for GetChars
 1708            // (don't have to check _throwOnOverflow for count or chars)
 01709            Debug.Assert(fallbackBuffer is null || fallbackBuffer.Remaining == 0,
 01710                "[UnicodeEncoding.GetChars]Expected empty fallback buffer at end");
 1711
 01712            return (int)(chars - charStart);
 1713        }
 1714
 1715        public override Encoder GetEncoder()
 1716        {
 01717            return new EncoderNLS(this);
 1718        }
 1719
 1720        public override Text.Decoder GetDecoder()
 1721        {
 01722            return new Decoder(this);
 1723        }
 1724
 1725        public override byte[] GetPreamble()
 1726        {
 01727            if (byteOrderMark)
 1728            {
 1729                // Note - we must allocate new byte[]'s here to prevent someone
 1730                // from modifying a cached byte[].
 01731                if (bigEndian)
 01732                    return [0xfe, 0xff];
 1733                else
 01734                    return [0xff, 0xfe];
 1735            }
 01736            return [];
 1737        }
 1738
 1739        public override ReadOnlySpan<byte> Preamble =>
 01740            GetType() != typeof(UnicodeEncoding) ? new ReadOnlySpan<byte>(GetPreamble()) : // in case a derived UnicodeE
 01741            !byteOrderMark ? default :
 01742            bigEndian ? [0xfe, 0xff] :
 01743            [0xff, 0xfe];
 1744
 1745        public override int GetMaxByteCount(int charCount)
 1746        {
 01747            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 1748
 1749            // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
 01750            long byteCount = (long)charCount + 1;
 1751
 01752            if (EncoderFallback.MaxCharCount > 1)
 01753                byteCount *= EncoderFallback.MaxCharCount;
 1754
 1755            // 2 bytes per char
 01756            byteCount <<= 1;
 1757
 01758            if (byteCount > 0x7fffffff)
 01759                throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
 1760
 01761            return (int)byteCount;
 1762        }
 1763
 1764        public override int GetMaxCharCount(int byteCount)
 1765        {
 01766            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 1767
 1768            // long because byteCount could be biggest int.
 1769            // 1 char per 2 bytes.  Round up in case 1 left over in decoder.
 1770            // Round up using &1 in case byteCount is max size
 1771            // Might also need an extra 1 if there's a left over high surrogate in the decoder.
 01772            long charCount = (long)(byteCount >> 1) + (byteCount & 1) + 1;
 1773
 1774            // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizarre like that)
 01775            if (DecoderFallback.MaxCharCount > 1)
 01776                charCount *= DecoderFallback.MaxCharCount;
 1777
 01778            if (charCount > 0x7fffffff)
 01779                throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
 1780
 01781            return (int)charCount;
 1782        }
 1783
 1784        public override bool Equals([NotNullWhen(true)] object? value)
 1785        {
 01786            if (value is UnicodeEncoding that)
 1787            {
 1788                //
 1789                // Big Endian Unicode has different code page (1201) than small Endian one (1200),
 1790                // so we still have to check _codePage here.
 1791                //
 01792                return (CodePage == that.CodePage) &&
 01793                        byteOrderMark == that.byteOrderMark &&
 01794                        // isThrowException == that.isThrowException &&  // Same as Encoder/Decoder being exception fall
 01795                        bigEndian == that.bigEndian &&
 01796                       (EncoderFallback.Equals(that.EncoderFallback)) &&
 01797                       (DecoderFallback.Equals(that.DecoderFallback));
 1798            }
 01799            return false;
 1800        }
 1801
 1802        public override int GetHashCode()
 1803        {
 01804            return CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
 01805                   (byteOrderMark ? 4 : 0) + (bigEndian ? 8 : 0);
 1806        }
 1807
 1808        private sealed class Decoder : DecoderNLS
 1809        {
 01810            internal int lastByte = -1;
 1811            internal char lastChar;
 1812
 01813            public Decoder(UnicodeEncoding encoding) : base(encoding)
 1814            {
 1815                // base calls reset
 01816            }
 1817
 1818            public override void Reset()
 1819            {
 01820                lastByte = -1;
 01821                lastChar = '\0';
 01822                _fallbackBuffer?.Reset();
 01823            }
 1824
 1825            // Anything left in our decoder?
 01826            internal override bool HasState => this.lastByte != -1 || this.lastChar != '\0';
 1827        }
 1828    }
 1829}
 1830