< Summary

Line coverage
18%
Covered lines: 129
Uncovered lines: 559
Coverable lines: 688
Total lines: 2834
Line coverage: 18.7%
Branch coverage
14%
Covered branches: 45
Total branches: 318
Branch coverage: 14.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .cctor()100%11100%
File 1: .ctor(...)100%11100%
File 1: .ctor()100%11100%
File 1: .ctor(...)0%440%
File 1: SetDefaultFallbacks()100%11100%
File 1: Convert(...)100%110%
File 1: Convert(...)100%110%
File 1: RegisterProvider(...)100%110%
File 1: GetEncoding(...)25%363619.04%
File 1: GetEncoding(...)50%2287.5%
File 1: GetEncoding(...)0%220%
File 1: GetEncoding(...)50%22100%
File 1: FilterDisallowedEncodings(...)33.33%6666.66%
File 1: GetEncodings()0%220%
File 1: GetPreamble()100%110%
File 1: GetDataItem()0%440%
File 1: Clone()100%110%
File 1: GetByteCount(...)100%110%
File 1: GetByteCount(...)0%220%
File 1: GetByteCount(...)0%220%
File 1: GetByteCount(...)100%110%
File 1: GetByteCount(...)100%110%
File 1: GetBytes(...)100%110%
File 1: GetBytes(...)100%110%
File 1: GetBytes(...)100%110%
File 1: GetBytes(...)0%440%
File 1: GetBytes(...)0%220%
File 1: GetBytes(...)0%220%
File 1: GetBytes(...)100%110%
File 1: TryGetBytes(...)0%220%
File 1: GetCharCount(...)100%110%
File 1: GetCharCount(...)100%110%
File 1: GetCharCount(...)100%110%
File 1: GetChars(...)100%110%
File 1: GetChars(...)100%110%
File 1: GetChars(...)0%220%
File 1: GetChars(...)100%110%
File 1: TryGetChars(...)0%220%
File 1: GetString(...)100%110%
File 1: GetString(...)100%11100%
File 1: IsAlwaysNormalized()100%110%
File 1: IsAlwaysNormalized(...)100%110%
File 1: GetDecoder()100%110%
File 1: GetEncoder()100%110%
File 1: GetString(...)100%110%
File 1: GetString(...)100%110%
File 1: Equals(...)0%660%
File 1: GetHashCode()100%110%
File 1: CreateTranscodingStream(...)100%110%
File 1: ThrowBytesOverflow()100%110%
File 1: ThrowBytesOverflow(...)0%880%
File 1: ThrowConversionOverflow()100%110%
File 1: ThrowCharsOverflow()100%110%
File 1: ThrowCharsOverflow(...)0%880%
File 1: .ctor(...)100%110%
File 1: GetByteCount(...)100%110%
File 1: GetByteCount(...)100%110%
File 1: GetBytes(...)100%110%
File 1: GetBytes(...)100%110%
File 1: .ctor(...)100%110%
File 1: GetCharCount(...)100%110%
File 1: GetCharCount(...)100%110%
File 1: GetCharCount(...)100%110%
File 1: GetChars(...)100%110%
File 1: GetChars(...)100%110%
File 1: GetChars(...)100%110%
File 1: .ctor(...)0%220%
File 1: AddChar(...)0%440%
File 1: AddChar(...)100%110%
File 1: AdjustBytes(...)100%110%
File 1: GetNextByte()0%220%
File 1: Fallback(...)100%110%
File 1: Fallback(...)0%440%
File 1: .ctor(...)0%880%
File 1: AddByte(...)0%440%
File 1: AddByte(...)100%110%
File 1: AddByte(...)100%110%
File 1: AddByte(...)0%220%
File 1: MovePrevious(...)0%10100%
File 1: GetNextChar()0%440%
File 2: DecodeFirstRune(...)100%110%
File 2: EncodeRune(...)100%110%
File 2: TryGetByteCount(...)100%110%
File 2: GetByteCount(...)0%880%
File 2: GetByteCountFast(...)100%110%
File 2: GetByteCountWithFallback(...)0%220%
File 2: GetByteCountWithFallback(...)0%880%
File 2: GetByteCountWithFallback(...)0%14140%
File 2: GetBytes(...)50%8892.3%
File 2: GetBytesFast(...)100%110%
File 2: GetBytesWithFallback(...)0%440%
File 2: GetBytesWithFallback(...)0%880%
File 2: GetBytesWithFallback(...)0%26260%
File 2: GetCharCount(...)0%10100%
File 2: GetCharCountFast(...)100%110%
File 2: GetCharCountWithFallback(...)50%22100%
File 2: GetCharCountWithFallback(...)0%10100%
File 2: GetCharCountWithFallback(...)71.42%141493.1%
File 2: GetChars(...)0%880%
File 2: GetCharsFast(...)100%110%
File 2: GetCharsWithFallback(...)50%44100%
File 2: GetCharsWithFallback(...)0%10100%
File 2: GetCharsWithFallback(...)65%202076.31%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Globalization;
 8using System.IO;
 9using System.Runtime.InteropServices;
 10using System.Runtime.Serialization;
 11
 12namespace System.Text
 13{
 14    // This abstract base class represents a character encoding. The class provides
 15    // methods to convert arrays and strings of Unicode characters to and from
 16    // arrays of bytes. A number of Encoding implementations are provided in
 17    // the System.Text package, including:
 18    //
 19    // ASCIIEncoding, which encodes Unicode characters as single 7-bit
 20    // ASCII characters. This encoding only supports character values between 0x00
 21    //     and 0x7F.
 22    // BaseCodePageEncoding, which encapsulates a Windows code page. Any
 23    //     installed code page can be accessed through this encoding, and conversions
 24    //     are performed using the WideCharToMultiByte and
 25    //     MultiByteToWideChar Windows API functions.
 26    // UnicodeEncoding, which encodes each Unicode character as two
 27    //    consecutive bytes. Both little-endian (code page 1200) and big-endian (code
 28    //    page 1201) encodings are recognized.
 29    // UTF7Encoding, which encodes Unicode characters using the UTF-7
 30    //     encoding (UTF-7 stands for UCS Transformation Format, 7-bit form). This
 31    //     encoding supports all Unicode character values, and can also be accessed
 32    //     as code page 65000.
 33    // UTF8Encoding, which encodes Unicode characters using the UTF-8
 34    //     encoding (UTF-8 stands for UCS Transformation Format, 8-bit form). This
 35    //     encoding supports all Unicode character values, and can also be accessed
 36    //     as code page 65001.
 37    // UTF32Encoding, both 12000 (little endian) & 12001 (big endian)
 38    //
 39    // In addition to directly instantiating Encoding objects, an
 40    // application can use the ForCodePage, GetASCII,
 41    // GetDefault, GetUnicode, GetUTF7, and GetUTF8
 42    // methods in this class to obtain encodings.
 43    //
 44    // Through an encoding, the GetBytes method is used to convert arrays
 45    // of characters to arrays of bytes, and the GetChars method is used to
 46    // convert arrays of bytes to arrays of characters. The GetBytes and
 47    // GetChars methods maintain no state between conversions, and are
 48    // generally intended for conversions of complete blocks of bytes and
 49    // characters in one operation. When the data to be converted is only available
 50    // in sequential blocks (such as data read from a stream) or when the amount of
 51    // data is so large that it needs to be divided into smaller blocks, an
 52    // application may choose to use a Decoder or an Encoder to
 53    // perform the conversion. Decoders and encoders allow sequential blocks of
 54    // data to be converted and they maintain the state required to support
 55    // conversions of data that spans adjacent blocks. Decoders and encoders are
 56    // obtained using the GetDecoder and GetEncoder methods.
 57    //
 58    // The core GetBytes and GetChars methods require the caller
 59    // to provide the destination buffer and ensure that the buffer is large enough
 60    // to hold the entire result of the conversion. When using these methods,
 61    // either directly on an Encoding object or on an associated
 62    // Decoder or Encoder, an application can use one of two methods
 63    // to allocate destination buffers.
 64    //
 65    // The GetByteCount and GetCharCount methods can be used to
 66    // compute the exact size of the result of a particular conversion, and an
 67    // appropriately sized buffer for that conversion can then be allocated.
 68    // The GetMaxByteCount and GetMaxCharCount methods can be
 69    // be used to compute the maximum possible size of a conversion of a given
 70    // number of bytes or characters, and a buffer of that size can then be reused
 71    // for multiple conversions.
 72    //
 73    // The first method generally uses less memory, whereas the second method
 74    // generally executes faster.
 75    //
 76
 77    public abstract partial class Encoding : ICloneable
 78    {
 79        // For netcore we use UTF8 as default encoding since ANSI isn't available
 180        private static readonly UTF8Encoding.UTF8EncodingSealed s_defaultEncoding = new UTF8Encoding.UTF8EncodingSealed(
 81
 82        // Returns an encoding for the system's current ANSI code page.
 183        public static Encoding Default => s_defaultEncoding;
 84
 85        //
 86        // The following values are from mlang.idl.  These values
 87        // should be in sync with those in mlang.idl.
 88        //
 89        internal const int MIMECONTF_MAILNEWS = 0x00000001;
 90        internal const int MIMECONTF_BROWSER = 0x00000002;
 91        internal const int MIMECONTF_SAVABLE_MAILNEWS = 0x00000100;
 92        internal const int MIMECONTF_SAVABLE_BROWSER = 0x00000200;
 93
 94        // Special Case Code Pages
 95        private const int CodePageDefault = 0;
 96        private const int CodePageNoOEM = 1;        // OEM Code page not supported
 97        private const int CodePageNoMac = 2;        // MAC code page not supported
 98        private const int CodePageNoThread = 3;        // Thread code page not supported
 99        private const int CodePageNoSymbol = 42;       // Symbol code page not supported
 100        private const int CodePageUnicode = 1200;     // Unicode
 101        private const int CodePageBigEndian = 1201;     // Big Endian Unicode
 102
 103        // Latin 1 & ASCII Code Pages
 104        internal const int CodePageASCII = 20127;    // ASCII
 105        internal const int ISO_8859_1 = 28591;    // Latin1
 106
 107        // Special code pages
 108        internal const int CodePageUTF7 = 65000;
 109        private const int CodePageUTF8 = 65001;
 110        private const int CodePageUTF32 = 12000;
 111        private const int CodePageUTF32BE = 12001;
 112
 113        internal int _codePage;
 114
 115        internal CodePageDataItem? _dataItem;
 116
 117        // Because of encoders we may be read only
 118        [OptionalField(VersionAdded = 2)]
 6119        private bool _isReadOnly = true;
 120
 121        // Encoding (encoder) fallback
 122        internal EncoderFallback encoderFallback;
 123        internal DecoderFallback decoderFallback;
 124
 1125        protected Encoding() : this(0)
 126        {
 1127        }
 128
 6129        protected Encoding(int codePage)
 130        {
 131            // Validate code page
 6132            ArgumentOutOfRangeException.ThrowIfNegative(codePage);
 133
 134            // Remember code page
 6135            _codePage = codePage;
 136
 137            // Use default encoder/decoder fallbacks
 6138            this.SetDefaultFallbacks();
 6139        }
 140
 141        // This constructor is needed to allow any sub-classing implementation to provide encoder/decoder fallback objec
 142        // because the encoding object is always created as read-only object and don't allow setting encoder/decoder fal
 143        // after the creation is done.
 0144        protected Encoding(int codePage, EncoderFallback? encoderFallback, DecoderFallback? decoderFallback)
 145        {
 146            // Validate code page
 0147            ArgumentOutOfRangeException.ThrowIfNegative(codePage);
 148
 149            // Remember code page
 0150            _codePage = codePage;
 151
 0152            this.encoderFallback = encoderFallback ?? EncoderFallback.ReplacementFallback;
 0153            this.decoderFallback = decoderFallback ?? DecoderFallback.ReplacementFallback;
 0154        }
 155
 156        // Default fallback that we'll use.
 157        [MemberNotNull(nameof(encoderFallback))]
 158        [MemberNotNull(nameof(decoderFallback))]
 159        internal virtual void SetDefaultFallbacks()
 160        {
 161            // For UTF-X encodings, we use a replacement fallback with an "\xFFFD" string,
 162            // For ASCII we use "?" replacement fallback, etc.
 1163            encoderFallback = EncoderFallback.ReplacementFallback;
 1164            decoderFallback = DecoderFallback.ReplacementFallback;
 1165        }
 166
 167        // Converts a byte array from one encoding to another. The bytes in the
 168        // bytes array are converted from srcEncoding to
 169        // dstEncoding, and the returned value is a new byte array
 170        // containing the result of the conversion.
 171        //
 172        public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, byte[] bytes)
 173        {
 0174            ArgumentNullException.ThrowIfNull(bytes);
 175
 0176            return Convert(srcEncoding, dstEncoding, bytes, 0, bytes.Length);
 177        }
 178
 179        // Converts a range of bytes in a byte array from one encoding to another.
 180        // This method converts count bytes from bytes starting at
 181        // index index from srcEncoding to dstEncoding, and
 182        // returns a new byte array containing the result of the conversion.
 183        //
 184        public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding,
 185            byte[] bytes, int index, int count)
 186        {
 0187            ArgumentNullException.ThrowIfNull(srcEncoding);
 0188            ArgumentNullException.ThrowIfNull(dstEncoding);
 0189            ArgumentNullException.ThrowIfNull(bytes);
 190
 0191            return dstEncoding.GetBytes(srcEncoding.GetChars(bytes, index, count));
 192        }
 193
 194        public static void RegisterProvider(EncodingProvider provider)
 195        {
 196            // Parameters validated inside EncodingProvider
 0197            EncodingProvider.AddProvider(provider);
 0198        }
 199
 200        public static Encoding GetEncoding(int codepage)
 201        {
 3202            Encoding? result = FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(codepage));
 3203            if (result is not null)
 0204                return result;
 205
 206            switch (codepage)
 207            {
 1208                case CodePageDefault: return Default;            // 0
 0209                case CodePageUnicode: return Unicode;            // 1200
 0210                case CodePageBigEndian: return BigEndianUnicode; // 1201
 0211                case CodePageUTF32: return UTF32;                // 12000
 0212                case CodePageUTF32BE: return BigEndianUTF32;     // 12001
 2213                case CodePageUTF8: return UTF8;                  // 65001
 0214                case CodePageASCII: return ASCII;                // 20127
 0215                case ISO_8859_1: return Latin1;                  // 28591
 216
 217                // We don't allow the following special code page values that Win32 allows.
 218                case CodePageNoOEM:                              // 1 CP_OEMCP
 219                case CodePageNoMac:                              // 2 CP_MACCP
 220                case CodePageNoThread:                           // 3 CP_THREAD_ACP
 221                case CodePageNoSymbol:                           // 42 CP_SYMBOL
 0222                    throw new ArgumentException(SR.Format(SR.Argument_CodepageNotSupported, codepage), nameof(codepage))
 223
 224                case CodePageUTF7:                               // 65000
 225                    {
 226                        // Support for UTF-7 is disabled by default. It can be re-enabled by registering a custom
 227                        // provider (which early-exits this method before the 'switch' statement) or by using
 228                        // AppContext. If support is not enabled, we'll provide a friendly error message stating
 229                        // how the developer can re-enable it in their application.
 230
 0231                        if (LocalAppContextSwitches.EnableUnsafeUTF7Encoding)
 232                        {
 233#pragma warning disable SYSLIB0001 // Encoding.UTF7 property getter is obsolete
 0234                            return UTF7;
 235#pragma warning restore SYSLIB0001
 236                        }
 237                        else
 238                        {
 0239                            string moreInfoUrl = string.Format(CultureInfo.InvariantCulture, Obsoletions.SharedUrlFormat
 0240                            string exceptionMessage = SR.Format(SR.Encoding_UTF7_Disabled, moreInfoUrl);
 0241                            throw new NotSupportedException(exceptionMessage); // matches generic "unknown code page" ex
 242                        }
 243                    }
 244            }
 245
 0246            if (codepage < 0 || codepage > 65535)
 247            {
 0248                throw new ArgumentOutOfRangeException(
 0249                    nameof(codepage), SR.Format(SR.ArgumentOutOfRange_Range, 0, 65535));
 250            }
 251
 0252            throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, codepage));
 253        }
 254
 255        public static Encoding GetEncoding(int codepage,
 256            EncoderFallback encoderFallback, DecoderFallback decoderFallback)
 257        {
 1258            Encoding? baseEncoding = FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(codepage, encode
 259
 1260            if (baseEncoding is not null)
 0261                return baseEncoding;
 262
 263            // Get the default encoding (which is cached and read only)
 1264            baseEncoding = GetEncoding(codepage);
 265
 266            // Clone it and set the fallback
 1267            Encoding fallbackEncoding = (Encoding)baseEncoding.Clone();
 1268            fallbackEncoding.EncoderFallback = encoderFallback;
 1269            fallbackEncoding.DecoderFallback = decoderFallback;
 270
 1271            return fallbackEncoding;
 272        }
 273
 274        // Returns an Encoding object for a given name or a given code page value.
 275        //
 276        public static Encoding GetEncoding(string name)
 277        {
 278            // NOTE: If you add a new encoding that can be requested by name, be sure to
 279            // add the corresponding item in EncodingTable.
 280            // Otherwise, the code below will throw exception when trying to call
 281            // EncodingTable.GetCodePageFromName().
 0282            return FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(name)) ??
 0283                GetEncoding(EncodingTable.GetCodePageFromName(name));
 284        }
 285
 286        // Returns an Encoding object for a given name or a given code page value.
 287        //
 288        public static Encoding GetEncoding(string name,
 289            EncoderFallback encoderFallback, DecoderFallback decoderFallback)
 290        {
 291            // NOTE: If you add a new encoding that can be requested by name, be sure to
 292            // add the corresponding item in EncodingTable.
 293            // Otherwise, the code below will throw exception when trying to call
 294            // EncodingTable.GetCodePageFromName().
 1295            return FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(name, encoderFallback, decoderFall
 1296                GetEncoding(EncodingTable.GetCodePageFromName(name), encoderFallback, decoderFallback);
 297        }
 298
 299        // If the input encoding is forbidden (currently, only UTF-7), returns null.
 300        // Otherwise returns the input encoding unchanged.
 301        private static Encoding? FilterDisallowedEncodings(Encoding? encoding)
 302        {
 5303            if (LocalAppContextSwitches.EnableUnsafeUTF7Encoding)
 304            {
 0305                return encoding;
 306            }
 307            else
 308            {
 5309                return (encoding?.CodePage == CodePageUTF7) ? null : encoding;
 310            }
 311        }
 312
 313        /// <summary>
 314        /// Get the <see cref="EncodingInfo"/> list from the runtime and all registered encoding providers
 315        /// </summary>
 316        /// <returns>The list of the <see cref="EncodingProvider"/> objects</returns>
 317        public static EncodingInfo[] GetEncodings()
 318        {
 0319            Dictionary<int, EncodingInfo>? result = EncodingProvider.GetEncodingListFromProviders();
 0320            return result is null ? EncodingTable.GetEncodings() : EncodingTable.GetEncodings(result);
 321        }
 322
 0323        public virtual byte[] GetPreamble() => [];
 324
 1325        public virtual ReadOnlySpan<byte> Preamble => GetPreamble();
 326
 327        private void GetDataItem()
 328        {
 0329            if (_dataItem is null)
 330            {
 0331                _dataItem = EncodingTable.GetCodePageDataItem(_codePage);
 0332                if (_dataItem is null)
 333                {
 0334                    throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, _codePage));
 335                }
 336            }
 0337        }
 338
 339        // Returns the name for this encoding that can be used with mail agent body tags.
 340        // If the encoding may not be used, the string is empty.
 341
 342        public virtual string BodyName
 343        {
 344            get
 345            {
 0346                if (_dataItem is null)
 347                {
 0348                    GetDataItem();
 349                }
 0350                return _dataItem!.BodyName;
 351            }
 352        }
 353
 354        // Returns the human-readable description of the encoding ( e.g. Hebrew (DOS)).
 355        public virtual string EncodingName
 356        {
 357            get
 358            {
 0359                if (_dataItem is null)
 360                {
 0361                    GetDataItem();
 362                }
 363
 0364                return _dataItem!.DisplayName;
 365            }
 366        }
 367
 368        // Returns the name for this encoding that can be used with mail agent header
 369        // tags.  If the encoding may not be used, the string is empty.
 370
 371        public virtual string HeaderName
 372        {
 373            get
 374            {
 0375                if (_dataItem is null)
 376                {
 0377                    GetDataItem();
 378                }
 0379                return _dataItem!.HeaderName;
 380            }
 381        }
 382
 383        // Returns the IANA preferred name for this encoding.
 384        public virtual string WebName
 385        {
 386            get
 387            {
 0388                if (_dataItem is null)
 389                {
 0390                    GetDataItem();
 391                }
 0392                return _dataItem!.WebName;
 393            }
 394        }
 395
 396        // Returns the windows code page that most closely corresponds to this encoding.
 397
 398        public virtual int WindowsCodePage
 399        {
 400            get
 401            {
 0402                if (_dataItem is null)
 403                {
 0404                    GetDataItem();
 405                }
 0406                return _dataItem!.UIFamilyCodePage;
 407            }
 408        }
 409
 410        // True if and only if the encoding is used for display by browsers clients.
 411
 412        public virtual bool IsBrowserDisplay
 413        {
 414            get
 415            {
 0416                if (_dataItem is null)
 417                {
 0418                    GetDataItem();
 419                }
 0420                return (_dataItem!.Flags & MIMECONTF_BROWSER) != 0;
 421            }
 422        }
 423
 424        // True if and only if the encoding is used for saving by browsers clients.
 425
 426        public virtual bool IsBrowserSave
 427        {
 428            get
 429            {
 0430                if (_dataItem is null)
 431                {
 0432                    GetDataItem();
 433                }
 0434                return (_dataItem!.Flags & MIMECONTF_SAVABLE_BROWSER) != 0;
 435            }
 436        }
 437
 438        // True if and only if the encoding is used for display by mail and news clients.
 439
 440        public virtual bool IsMailNewsDisplay
 441        {
 442            get
 443            {
 0444                if (_dataItem is null)
 445                {
 0446                    GetDataItem();
 447                }
 0448                return (_dataItem!.Flags & MIMECONTF_MAILNEWS) != 0;
 449            }
 450        }
 451
 452        // True if and only if the encoding is used for saving documents by mail and
 453        // news clients
 454
 455        public virtual bool IsMailNewsSave
 456        {
 457            get
 458            {
 0459                if (_dataItem is null)
 460                {
 0461                    GetDataItem();
 462                }
 0463                return (_dataItem!.Flags & MIMECONTF_SAVABLE_MAILNEWS) != 0;
 464            }
 465        }
 466
 467        // True if and only if the encoding only uses single byte code points.  (Ie, ASCII, 1252, etc)
 468
 0469        public virtual bool IsSingleByte => false;
 470
 471        public EncoderFallback EncoderFallback
 472        {
 1473            get => encoderFallback;
 474            set
 475            {
 1476                if (this.IsReadOnly)
 0477                    throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
 478
 1479                ArgumentNullException.ThrowIfNull(value);
 480
 1481                encoderFallback = value;
 1482            }
 483        }
 484
 485        public DecoderFallback DecoderFallback
 486        {
 24095487            get => decoderFallback;
 488            set
 489            {
 1490                if (this.IsReadOnly)
 0491                    throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
 492
 1493                ArgumentNullException.ThrowIfNull(value);
 494
 1495                decoderFallback = value;
 1496            }
 497        }
 498
 499        public virtual object Clone()
 500        {
 0501            Encoding newEncoding = (Encoding)this.MemberwiseClone();
 502
 503            // New one should be readable
 0504            newEncoding._isReadOnly = false;
 0505            return newEncoding;
 506        }
 507
 508        public bool IsReadOnly
 509        {
 2510            get => _isReadOnly;
 1511            private protected set => _isReadOnly = value;
 512        }
 513
 514        // Returns an encoding for the ASCII character set. The returned encoding
 515        // will be an instance of the ASCIIEncoding class.
 516
 0517        public static Encoding ASCII => ASCIIEncoding.s_default;
 518
 519        /// <summary>Gets an encoding for the Latin1 character set (ISO-8859-1).</summary>
 0520        public static Encoding Latin1 => Latin1Encoding.s_default;
 521
 522        // Returns the number of bytes required to encode the given character
 523        // array.
 524        //
 525        public virtual int GetByteCount(char[] chars)
 526        {
 0527            ArgumentNullException.ThrowIfNull(chars);
 528
 0529            return GetByteCount(chars, 0, chars.Length);
 530        }
 531
 532        public virtual int GetByteCount(string s)
 533        {
 0534            if (s is null)
 535            {
 0536                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
 537            }
 538
 0539            char[] chars = s.ToCharArray();
 0540            return GetByteCount(chars, 0, chars.Length);
 541        }
 542
 543        // Returns the number of bytes required to encode a range of characters in
 544        // a character array.
 545        //
 546        public abstract int GetByteCount(char[] chars, int index, int count);
 547
 548        // Returns the number of bytes required to encode a string range.
 549        //
 550        public int GetByteCount(string s, int index, int count)
 551        {
 0552            ArgumentNullException.ThrowIfNull(s);
 0553            ArgumentOutOfRangeException.ThrowIfNegative(index);
 0554            ArgumentOutOfRangeException.ThrowIfNegative(count);
 0555            ArgumentOutOfRangeException.ThrowIfGreaterThan(index, s.Length - count);
 556
 557            unsafe
 558            {
 0559                fixed (char* pChar = s)
 560                {
 0561                    return GetByteCount(pChar + index, count);
 562                }
 563            }
 564        }
 565
 566        // We expect this to be the workhorse for NLS encodings
 567        // unfortunately for existing overrides, it has to call the [] version,
 568        // which is really slow, so this method should be avoided if you're calling
 569        // a 3rd party encoding.
 570        [CLSCompliant(false)]
 571        public virtual unsafe int GetByteCount(char* chars, int count)
 572        {
 0573            ArgumentNullException.ThrowIfNull(chars);
 0574            ArgumentOutOfRangeException.ThrowIfNegative(count);
 575
 0576            char[] arrChar = new ReadOnlySpan<char>(chars, count).ToArray();
 577
 0578            return GetByteCount(arrChar, 0, count);
 579        }
 580
 581        public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars)
 0582        {
 0583            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
 584            {
 0585                return GetByteCount(charsPtr, chars.Length);
 586            }
 587        }
 588
 589        // Returns a byte array containing the encoded representation of the given
 590        // character array.
 591        //
 592        public virtual byte[] GetBytes(char[] chars)
 593        {
 0594            ArgumentNullException.ThrowIfNull(chars);
 595
 0596            return GetBytes(chars, 0, chars.Length);
 597        }
 598
 599        // Returns a byte array containing the encoded representation of a range
 600        // of characters in a character array.
 601        //
 602        public virtual byte[] GetBytes(char[] chars, int index, int count)
 603        {
 0604            byte[] result = new byte[GetByteCount(chars, index, count)];
 0605            GetBytes(chars, index, count, result, 0);
 0606            return result;
 607        }
 608
 609        // Encodes a range of characters in a character array into a range of bytes
 610        // in a byte array. An exception occurs if the byte array is not large
 611        // enough to hold the complete encoding of the characters. The
 612        // GetByteCount method can be used to determine the exact number of
 613        // bytes that will be produced for a given range of characters.
 614        // Alternatively, the GetMaxByteCount method can be used to
 615        // determine the maximum number of bytes that will be produced for a given
 616        // number of characters, regardless of the actual character values.
 617        //
 618        public abstract int GetBytes(char[] chars, int charIndex, int charCount,
 619            byte[] bytes, int byteIndex);
 620
 621        // Returns a byte array containing the encoded representation of the given
 622        // string.
 623        //
 624        public virtual byte[] GetBytes(string s)
 625        {
 0626            ArgumentNullException.ThrowIfNull(s);
 627
 0628            int byteCount = GetByteCount(s);
 0629            byte[] bytes = new byte[byteCount];
 0630            int bytesReceived = GetBytes(s, 0, s.Length, bytes, 0);
 0631            Debug.Assert(byteCount == bytesReceived);
 0632            return bytes;
 633        }
 634
 635        // Returns a byte array containing the encoded representation of the given
 636        // string range.
 637        //
 638        public byte[] GetBytes(string s, int index, int count)
 639        {
 0640            ArgumentNullException.ThrowIfNull(s);
 0641            ArgumentOutOfRangeException.ThrowIfNegative(index);
 0642            ArgumentOutOfRangeException.ThrowIfNegative(count);
 0643            ArgumentOutOfRangeException.ThrowIfGreaterThan(index, s.Length - count);
 644
 645            unsafe
 646            {
 0647                fixed (char* pChar = s)
 648                {
 0649                    int byteCount = GetByteCount(pChar + index, count);
 0650                    if (byteCount == 0)
 0651                        return [];
 652
 0653                    byte[] bytes = new byte[byteCount];
 0654                    fixed (byte* pBytes = &bytes[0])
 655                    {
 0656                        int bytesReceived = GetBytes(pChar + index, count, pBytes, byteCount);
 0657                        Debug.Assert(byteCount == bytesReceived);
 658                    }
 0659                    return bytes;
 660                }
 661            }
 662        }
 663
 664        public virtual int GetBytes(string s, int charIndex, int charCount,
 665                                    byte[] bytes, int byteIndex)
 666        {
 0667            if (s is null)
 668            {
 0669                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
 670            }
 671
 0672            return GetBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex);
 673        }
 674
 675        // We expect this to be the workhorse for NLS Encodings, but for existing
 676        // ones we need a working (if slow) default implementation)
 677        //
 678        // WARNING WARNING WARNING
 679        //
 680        // WARNING: If this breaks it could be a security threat.  Obviously we
 681        // call this internally, so you need to make sure that your pointers, counts
 682        // and indexes are correct when you call this method.
 683        //
 684        // In addition, we have internal code, which will be marked as "safe" calling
 685        // this code.  However this code is dependent upon the implementation of an
 686        // external GetBytes() method, which could be overridden by a third party and
 687        // the results of which cannot be guaranteed.  We use that result to copy
 688        // the byte[] to our byte* output buffer.  If the result count was wrong, we
 689        // could easily overflow our output buffer.  Therefore we do an extra test
 690        // when we copy the buffer so that we don't overflow byteCount either.
 691
 692        [CLSCompliant(false)]
 693        public virtual unsafe int GetBytes(char* chars, int charCount,
 694                                              byte* bytes, int byteCount)
 695        {
 0696            ArgumentNullException.ThrowIfNull(chars);
 0697            ArgumentNullException.ThrowIfNull(bytes);
 698
 0699            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 0700            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 701
 702            // Get the char array to convert
 0703            char[] arrChar = new ReadOnlySpan<char>(chars, charCount).ToArray();
 704
 705            // Get the byte array to fill
 0706            byte[] arrByte = new byte[byteCount];
 707
 708            // Do the work
 0709            int result = GetBytes(arrChar, 0, charCount, arrByte, 0);
 710
 0711            Debug.Assert(result <= byteCount, "[Encoding.GetBytes]Returned more bytes than we have space for");
 712
 713            // Copy the byte array
 714            // WARNING: We MUST make sure that we don't copy too many bytes.  We can't
 715            // rely on result because it could be a 3rd party implementation.  We need
 716            // to make sure we never copy more than byteCount bytes no matter the value
 717            // of result
 0718            if (result < byteCount)
 0719                byteCount = result;
 720
 721            // Copy the data, don't overrun our array!
 0722            new ReadOnlySpan<byte>(arrByte, 0, byteCount).CopyTo(new Span<byte>(bytes, byteCount));
 723
 0724            return byteCount;
 725        }
 726
 727        public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
 0728        {
 0729            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
 0730            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
 731            {
 0732                return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
 733            }
 734        }
 735
 736        /// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destinati
 737        /// <param name="chars">The span containing the set of characters to encode.</param>
 738        /// <param name="bytes">The byte span to hold the encoded bytes.</param>
 739        /// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <pa
 740        /// <returns><see langword="true"/> if all of the characters were encoded into the destination; <see langword="f
 741        public virtual bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
 742        {
 0743            int required = GetByteCount(chars);
 0744            if (required <= bytes.Length)
 745            {
 0746                bytesWritten = GetBytes(chars, bytes);
 0747                return true;
 748            }
 749
 0750            bytesWritten = 0;
 0751            return false;
 752        }
 753
 754        // Returns the number of characters produced by decoding the given byte
 755        // array.
 756        //
 757        public virtual int GetCharCount(byte[] bytes)
 758        {
 0759            ArgumentNullException.ThrowIfNull(bytes);
 760
 0761            return GetCharCount(bytes, 0, bytes.Length);
 762        }
 763
 764        // Returns the number of characters produced by decoding a range of bytes
 765        // in a byte array.
 766        //
 767        public abstract int GetCharCount(byte[] bytes, int index, int count);
 768
 769        // We expect this to be the workhorse for NLS Encodings, but for existing
 770        // ones we need a working (if slow) default implementation)
 771        [CLSCompliant(false)]
 772        public virtual unsafe int GetCharCount(byte* bytes, int count)
 773        {
 0774            ArgumentNullException.ThrowIfNull(bytes);
 775
 0776            ArgumentOutOfRangeException.ThrowIfNegative(count);
 777
 0778            byte[] arrByte = new ReadOnlySpan<byte>(bytes, count).ToArray();
 779
 0780            return GetCharCount(arrByte, 0, count);
 781        }
 782
 783        public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes)
 0784        {
 0785            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
 786            {
 0787                return GetCharCount(bytesPtr, bytes.Length);
 788            }
 789        }
 790
 791        // Returns a character array containing the decoded representation of a
 792        // given byte array.
 793        //
 794        public virtual char[] GetChars(byte[] bytes)
 795        {
 0796            ArgumentNullException.ThrowIfNull(bytes);
 797
 0798            return GetChars(bytes, 0, bytes.Length);
 799        }
 800
 801        // Returns a character array containing the decoded representation of a
 802        // range of bytes in a byte array.
 803        //
 804        public virtual char[] GetChars(byte[] bytes, int index, int count)
 805        {
 0806            char[] result = new char[GetCharCount(bytes, index, count)];
 0807            GetChars(bytes, index, count, result, 0);
 0808            return result;
 809        }
 810
 811        // Decodes a range of bytes in a byte array into a range of characters in a
 812        // character array. An exception occurs if the character array is not large
 813        // enough to hold the complete decoding of the bytes. The
 814        // GetCharCount method can be used to determine the exact number of
 815        // characters that will be produced for a given range of bytes.
 816        // Alternatively, the GetMaxCharCount method can be used to
 817        // determine the maximum number of characters that will be produced for a
 818        // given number of bytes, regardless of the actual byte values.
 819        //
 820
 821        public abstract int GetChars(byte[] bytes, int byteIndex, int byteCount,
 822                                       char[] chars, int charIndex);
 823
 824        // We expect this to be the workhorse for NLS Encodings, but for existing
 825        // ones we need a working (if slow) default implementation)
 826        //
 827        // WARNING WARNING WARNING
 828        //
 829        // WARNING: If this breaks it could be a security threat.  Obviously we
 830        // call this internally, so you need to make sure that your pointers, counts
 831        // and indexes are correct when you call this method.
 832        //
 833        // In addition, we have internal code, which will be marked as "safe" calling
 834        // this code.  However this code is dependent upon the implementation of an
 835        // external GetChars() method, which could be overridden by a third party and
 836        // the results of which cannot be guaranteed.  We use that result to copy
 837        // the char[] to our char* output buffer.  If the result count was wrong, we
 838        // could easily overflow our output buffer.  Therefore we do an extra test
 839        // when we copy the buffer so that we don't overflow charCount either.
 840
 841        [CLSCompliant(false)]
 842        public virtual unsafe int GetChars(byte* bytes, int byteCount,
 843                                              char* chars, int charCount)
 844        {
 0845            ArgumentNullException.ThrowIfNull(bytes);
 0846            ArgumentNullException.ThrowIfNull(chars);
 847
 0848            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 0849            ArgumentOutOfRangeException.ThrowIfNegative(charCount);
 850
 851            // Get the byte array to convert
 0852            byte[] arrByte = new ReadOnlySpan<byte>(bytes, byteCount).ToArray();
 853
 854            // Get the char array to fill
 0855            char[] arrChar = new char[charCount];
 856
 857            // Do the work
 0858            int result = GetChars(arrByte, 0, byteCount, arrChar, 0);
 859
 0860            Debug.Assert(result <= charCount, "[Encoding.GetChars]Returned more chars than we have space for");
 861
 862            // Copy the char array
 863            // WARNING: We MUST make sure that we don't copy too many chars.  We can't
 864            // rely on result because it could be a 3rd party implementation.  We need
 865            // to make sure we never copy more than charCount chars no matter the value
 866            // of result
 0867            if (result < charCount)
 0868                charCount = result;
 869
 870            // Copy the data, don't overrun our array!
 0871            new ReadOnlySpan<char>(arrChar, 0, charCount).CopyTo(new Span<char>(chars, charCount));
 872
 0873            return charCount;
 874        }
 875
 876        public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
 0877        {
 0878            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
 0879            fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
 880            {
 0881                return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
 882            }
 883        }
 884
 885        /// <summary>Decodes into a span of chars a set of bytes from the specified read-only span if the destination is
 886        /// <param name="bytes">A read-only span containing the sequence of bytes to decode.</param>
 887        /// <param name="chars">The character span receiving the decoded bytes.</param>
 888        /// <param name="charsWritten">Upon successful completion of the operation, the number of chars decoded into <pa
 889        /// <returns><see langword="true"/> if all of the characters were decoded into the destination; <see langword="f
 890        public virtual bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
 891        {
 0892            int required = GetCharCount(bytes);
 0893            if (required <= chars.Length)
 894            {
 0895                charsWritten = GetChars(bytes, chars);
 0896                return true;
 897            }
 898
 0899            charsWritten = 0;
 0900            return false;
 901        }
 902
 903        [CLSCompliant(false)]
 904        public unsafe string GetString(byte* bytes, int byteCount)
 905        {
 0906            ArgumentNullException.ThrowIfNull(bytes);
 907
 0908            ArgumentOutOfRangeException.ThrowIfNegative(byteCount);
 909
 0910            return string.CreateStringFromEncoding(bytes, byteCount, this);
 911        }
 912
 913        public unsafe string GetString(ReadOnlySpan<byte> bytes)
 6497914        {
 6497915            fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
 916            {
 6497917                return string.CreateStringFromEncoding(bytesPtr, bytes.Length, this);
 918            }
 919        }
 920
 921        // Returns the code page identifier of this encoding. The returned value is
 922        // an integer between 0 and 65535 if the encoding has a code page
 923        // identifier, or -1 if the encoding does not represent a code page.
 924        //
 925
 4926        public virtual int CodePage => _codePage;
 927
 928        // Quick accessor for "is UTF8?"
 0929        internal bool IsUTF8CodePage => CodePage == CodePageUTF8;
 930
 931        // IsAlwaysNormalized
 932        // Returns true if the encoding is always normalized for the specified encoding form
 933        public bool IsAlwaysNormalized() =>
 0934            IsAlwaysNormalized(NormalizationForm.FormC);
 935
 936        public virtual bool IsAlwaysNormalized(NormalizationForm form) =>
 937            // Assume false unless the encoding knows otherwise
 0938            false;
 939
 940        // Returns a Decoder object for this encoding. The returned object
 941        // can be used to decode a sequence of bytes into a sequence of characters.
 942        // Contrary to the GetChars family of methods, a Decoder can
 943        // convert partial sequences of bytes into partial sequences of characters
 944        // by maintaining the appropriate state between the conversions.
 945        //
 946        // This default implementation returns a Decoder that simply
 947        // forwards calls to the GetCharCount and GetChars methods to
 948        // the corresponding methods of this encoding. Encodings that require state
 949        // to be maintained between successive conversions should override this
 950        // method and return an instance of an appropriate Decoder
 951        // implementation.
 952        //
 953
 0954        public virtual Decoder GetDecoder() => new DefaultDecoder(this);
 955
 956        // Returns an Encoder object for this encoding. The returned object
 957        // can be used to encode a sequence of characters into a sequence of bytes.
 958        // Contrary to the GetBytes family of methods, an Encoder can
 959        // convert partial sequences of characters into partial sequences of bytes
 960        // by maintaining the appropriate state between the conversions.
 961        //
 962        // This default implementation returns an Encoder that simply
 963        // forwards calls to the GetByteCount and GetBytes methods to
 964        // the corresponding methods of this encoding. Encodings that require state
 965        // to be maintained between successive conversions should override this
 966        // method and return an instance of an appropriate Encoder
 967        // implementation.
 968        //
 969
 0970        public virtual Encoder GetEncoder() => new DefaultEncoder(this);
 971
 972        // Returns the maximum number of bytes required to encode a given number of
 973        // characters. This method can be used to determine an appropriate buffer
 974        // size for byte arrays passed to the GetBytes method of this
 975        // encoding or the GetBytes method of an Encoder for this
 976        // encoding. All encodings must guarantee that no buffer overflow
 977        // exceptions will occur if buffers are sized according to the results of
 978        // this method.
 979        //
 980        // WARNING: If you're using something besides the default replacement encoder fallback,
 981        // then you could have more bytes than this returned from an actual call to GetBytes().
 982        //
 983        public abstract int GetMaxByteCount(int charCount);
 984
 985        // Returns the maximum number of characters produced by decoding a given
 986        // number of bytes. This method can be used to determine an appropriate
 987        // buffer size for character arrays passed to the GetChars method of
 988        // this encoding or the GetChars method of a Decoder for this
 989        // encoding. All encodings must guarantee that no buffer overflow
 990        // exceptions will occur if buffers are sized according to the results of
 991        // this method.
 992        //
 993        public abstract int GetMaxCharCount(int byteCount);
 994
 995        // Returns a string containing the decoded representation of a given byte
 996        // array.
 997        //
 998        public virtual string GetString(byte[] bytes)
 999        {
 01000            ArgumentNullException.ThrowIfNull(bytes);
 1001
 01002            return GetString(bytes, 0, bytes.Length);
 1003        }
 1004
 1005        // Returns a string containing the decoded representation of a range of
 1006        // bytes in a byte array.
 1007        //
 1008        // Internally we override this for performance
 1009        //
 1010        public virtual string GetString(byte[] bytes, int index, int count) =>
 01011            new string(GetChars(bytes, index, count));
 1012
 1013        // Returns an encoding for Unicode format. The returned encoding will be
 1014        // an instance of the UnicodeEncoding class.
 1015        //
 1016        // It will use little endian byte order, but will detect
 1017        // input in big endian if it finds a byte order mark per Unicode 2.0.
 1018
 11019        public static Encoding Unicode => UnicodeEncoding.s_littleEndianDefault;
 1020
 1021        // Returns an encoding for Unicode format. The returned encoding will be
 1022        // an instance of the UnicodeEncoding class.
 1023        //
 1024        // It will use big endian byte order, but will detect
 1025        // input in little endian if it finds a byte order mark per Unicode 2.0.
 1026
 01027        public static Encoding BigEndianUnicode => UnicodeEncoding.s_bigEndianDefault;
 1028
 1029        // Returns an encoding for the UTF-7 format. The returned encoding will be
 1030        // an instance of the UTF7Encoding class.
 1031
 1032        [Obsolete(Obsoletions.SystemTextEncodingUTF7Message, DiagnosticId = Obsoletions.SystemTextEncodingUTF7DiagId, Ur
 01033        public static Encoding UTF7 => UTF7Encoding.s_default;
 1034
 1035        // Returns an encoding for the UTF-8 format. The returned encoding will be
 1036        // an instance of the UTF8Encoding class.
 1037
 122611038        public static Encoding UTF8 => UTF8Encoding.s_default;
 1039
 1040        // Returns an encoding for the UTF-32 format. The returned encoding will be
 1041        // an instance of the UTF32Encoding class.
 1042
 01043        public static Encoding UTF32 => UTF32Encoding.s_default;
 1044
 1045        // Returns an encoding for the UTF-32 format. The returned encoding will be
 1046        // an instance of the UTF32Encoding class.
 1047        //
 1048        // It will use big endian byte order.
 1049
 01050        private static Encoding BigEndianUTF32 => UTF32Encoding.s_bigEndianDefault;
 1051
 1052        public override bool Equals([NotNullWhen(true)] object? value) =>
 01053            value is Encoding that &&
 01054            (_codePage == that._codePage) &&
 01055            (EncoderFallback.Equals(that.EncoderFallback)) &&
 01056            (DecoderFallback.Equals(that.DecoderFallback));
 1057
 1058        public override int GetHashCode() =>
 01059            _codePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode();
 1060
 1061        /// <summary>
 1062        /// Creates a <see cref="Stream"/> which serves to transcode data between an inner <see cref="Encoding"/>
 1063        /// and an outer <see cref="Encoding"/>, similar to <see cref="Convert"/>.
 1064        /// </summary>
 1065        /// <param name="innerStream">The <see cref="Stream"/> to wrap.</param>
 1066        /// <param name="innerStreamEncoding">The <see cref="Encoding"/> associated with <paramref name="innerStream"/>.
 1067        /// <param name="outerStreamEncoding">The <see cref="Encoding"/> associated with the <see cref="Stream"/> return
 1068        /// by this method.</param>
 1069        /// <param name="leaveOpen"><see langword="true"/> if disposing the <see cref="Stream"/> returned by this method
 1070        /// should <em>not</em> dispose <paramref name="innerStream"/>.</param>
 1071        /// <returns>A <see cref="Stream"/> which transcodes the contents of <paramref name="innerStream"/>
 1072        /// as <paramref name="outerStreamEncoding"/>.</returns>
 1073        /// <remarks>
 1074        /// The returned <see cref="Stream"/>'s <see cref="Stream.CanRead"/> and <see cref="Stream.CanWrite"/> propertie
 1075        /// will reflect whether <paramref name="innerStream"/> is readable or writable. If <paramref name="innerStream"
 1076        /// is full-duplex, the returned <see cref="Stream"/> will be as well. However, the returned <see cref="Stream"/
 1077        /// is not seekable, even if <paramref name="innerStream"/>'s <see cref="Stream.CanSeek"/> property returns <see
 1078        /// </remarks>
 1079        public static Stream CreateTranscodingStream(Stream innerStream, Encoding innerStreamEncoding, Encoding outerStr
 1080        {
 01081            ArgumentNullException.ThrowIfNull(innerStream);
 01082            ArgumentNullException.ThrowIfNull(innerStreamEncoding);
 01083            ArgumentNullException.ThrowIfNull(outerStreamEncoding);
 1084
 1085            // We can't entirely optimize away the case where innerStreamEncoding == outerStreamEncoding. For example,
 1086            // the Encoding might perform a lossy conversion when it sees invalid data, so we still need to call it
 1087            // to perform basic validation. It's also possible that somebody subclassed one of the built-in types
 1088            // like ASCIIEncoding or UTF8Encoding and is running some non-standard logic. If this becomes a bottleneck
 1089            // we can consider targeted optimizations in a future release.
 1090
 01091            return new TranscodingStream(innerStream, innerStreamEncoding, outerStreamEncoding, leaveOpen);
 1092        }
 1093
 1094        [DoesNotReturn]
 1095        internal void ThrowBytesOverflow() =>
 1096            // Special message to include fallback type in case fallback's GetMaxCharCount is broken
 1097            // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount
 01098            throw new ArgumentException(
 01099                SR.Format(SR.Argument_EncodingConversionOverflowBytes, _codePage, EncoderFallback.GetType()), "bytes");
 1100
 1101        internal void ThrowBytesOverflow(EncoderNLS? encoder, bool nothingEncoded)
 1102        {
 01103            if (encoder is null || encoder._throwOnOverflow || nothingEncoded)
 1104            {
 01105                if (encoder is not null && encoder.InternalHasFallbackBuffer)
 01106                    encoder.FallbackBuffer.InternalReset();
 1107                // Special message to include fallback type in case fallback's GetMaxCharCount is broken
 1108                // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount
 01109                ThrowBytesOverflow();
 1110            }
 1111
 1112            // If we didn't throw, we are in convert and have to remember our flushing
 01113            encoder.ClearMustFlush();
 01114        }
 1115
 1116        [DoesNotReturn]
 1117        [StackTraceHidden]
 1118        internal static void ThrowConversionOverflow() =>
 01119            throw new ArgumentException(SR.Argument_ConversionOverflow);
 1120
 1121        [DoesNotReturn]
 1122        [StackTraceHidden]
 1123        internal void ThrowCharsOverflow() =>
 1124            // Special message to include fallback type in case fallback's GetMaxCharCount is broken
 1125            // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount
 01126            throw new ArgumentException(
 01127                SR.Format(SR.Argument_EncodingConversionOverflowChars, _codePage, DecoderFallback.GetType()), "chars");
 1128
 1129        internal void ThrowCharsOverflow(DecoderNLS? decoder, bool nothingDecoded)
 1130        {
 01131            if (decoder is null || decoder._throwOnOverflow || nothingDecoded)
 1132            {
 01133                if (decoder is not null && decoder.InternalHasFallbackBuffer)
 01134                    decoder.FallbackBuffer.InternalReset();
 1135
 1136                // Special message to include fallback type in case fallback's GetMaxCharCount is broken
 1137                // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount
 01138                ThrowCharsOverflow();
 1139            }
 1140
 1141            // If we didn't throw, we are in convert and have to remember our flushing
 01142            decoder.ClearMustFlush();
 01143        }
 1144
 1145        internal sealed class DefaultEncoder : Encoder
 1146        {
 1147            private readonly Encoding _encoding;
 1148
 01149            public DefaultEncoder(Encoding encoding)
 1150            {
 01151                _encoding = encoding;
 01152            }
 1153
 1154            // Returns the number of bytes the next call to GetBytes will
 1155            // produce if presented with the given range of characters and the given
 1156            // value of the flush parameter. The returned value takes into
 1157            // account the state in which the encoder was left following the last call
 1158            // to GetBytes. The state of the encoder is not affected by a call
 1159            // to this method.
 1160            //
 1161
 1162            public override int GetByteCount(char[] chars, int index, int count, bool flush) =>
 01163                _encoding.GetByteCount(chars, index, count);
 1164
 1165            public override unsafe int GetByteCount(char* chars, int count, bool flush) =>
 01166                _encoding.GetByteCount(chars, count);
 1167
 1168            // Encodes a range of characters in a character array into a range of bytes
 1169            // in a byte array. The method encodes charCount characters from
 1170            // chars starting at index charIndex, storing the resulting
 1171            // bytes in bytes starting at index byteIndex. The encoding
 1172            // takes into account the state in which the encoder was left following the
 1173            // last call to this method. The flush parameter indicates whether
 1174            // the encoder should flush any shift-states and partial characters at the
 1175            // end of the conversion. To ensure correct termination of a sequence of
 1176            // blocks of encoded bytes, the last call to GetBytes should specify
 1177            // a value of true for the flush parameter.
 1178            //
 1179            // An exception occurs if the byte array is not large enough to hold the
 1180            // complete encoding of the characters. The GetByteCount method can
 1181            // be used to determine the exact number of bytes that will be produced for
 1182            // a given range of characters. Alternatively, the GetMaxByteCount
 1183            // method of the Encoding that produced this encoder can be used to
 1184            // determine the maximum number of bytes that will be produced for a given
 1185            // number of characters, regardless of the actual character values.
 1186            //
 1187
 1188            public override int GetBytes(char[] chars, int charIndex, int charCount,
 1189                                         byte[] bytes, int byteIndex, bool flush) =>
 01190                _encoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
 1191
 1192            public override unsafe int GetBytes(char* chars, int charCount,
 1193                                                byte* bytes, int byteCount, bool flush) =>
 01194                _encoding.GetBytes(chars, charCount, bytes, byteCount);
 1195        }
 1196
 1197        internal sealed class DefaultDecoder : Decoder
 1198        {
 1199            private readonly Encoding _encoding;
 1200
 01201            public DefaultDecoder(Encoding encoding)
 1202            {
 01203                _encoding = encoding;
 01204            }
 1205
 1206            // Returns the number of characters the next call to GetChars will
 1207            // produce if presented with the given range of bytes. The returned value
 1208            // takes into account the state in which the decoder was left following the
 1209            // last call to GetChars. The state of the decoder is not affected
 1210            // by a call to this method.
 1211            //
 1212
 1213            public override int GetCharCount(byte[] bytes, int index, int count) =>
 01214                GetCharCount(bytes, index, count, false);
 1215
 1216            public override int GetCharCount(byte[] bytes, int index, int count, bool flush) =>
 01217                _encoding.GetCharCount(bytes, index, count);
 1218
 1219            public override unsafe int GetCharCount(byte* bytes, int count, bool flush) =>
 1220                // By default just call the encoding version, no flush by default
 01221                _encoding.GetCharCount(bytes, count);
 1222
 1223            // Decodes a range of bytes in a byte array into a range of characters
 1224            // in a character array. The method decodes byteCount bytes from
 1225            // bytes starting at index byteIndex, storing the resulting
 1226            // characters in chars starting at index charIndex. The
 1227            // decoding takes into account the state in which the decoder was left
 1228            // following the last call to this method.
 1229            //
 1230            // An exception occurs if the character array is not large enough to
 1231            // hold the complete decoding of the bytes. The GetCharCount method
 1232            // can be used to determine the exact number of characters that will be
 1233            // produced for a given range of bytes. Alternatively, the
 1234            // GetMaxCharCount method of the Encoding that produced this
 1235            // decoder can be used to determine the maximum number of characters that
 1236            // will be produced for a given number of bytes, regardless of the actual
 1237            // byte values.
 1238            //
 1239
 1240            public override int GetChars(byte[] bytes, int byteIndex, int byteCount,
 1241                                         char[] chars, int charIndex) =>
 01242                GetChars(bytes, byteIndex, byteCount, chars, charIndex, false);
 1243
 1244            public override int GetChars(byte[] bytes, int byteIndex, int byteCount,
 1245                                         char[] chars, int charIndex, bool flush) =>
 01246                _encoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
 1247
 1248            public override unsafe int GetChars(byte* bytes, int byteCount,
 1249                                                char* chars, int charCount, bool flush) =>
 1250                // By default just call the encoding's version
 01251                _encoding.GetChars(bytes, byteCount, chars, charCount);
 1252        }
 1253
 1254        internal sealed class EncodingCharBuffer
 1255        {
 1256            private unsafe char* _chars;
 1257            private readonly unsafe char* _charStart;
 1258            private readonly unsafe char* _charEnd;
 1259            private int _charCountResult;
 1260            private readonly Encoding _enc;
 1261            private readonly DecoderNLS? _decoder;
 1262            private readonly unsafe byte* _byteStart;
 1263            private readonly unsafe byte* _byteEnd;
 1264            private unsafe byte* _bytes;
 1265            private readonly DecoderFallbackBuffer _fallbackBuffer;
 1266
 01267            internal unsafe EncodingCharBuffer(Encoding enc, DecoderNLS? decoder, char* charStart, int charCount,
 01268                                                    byte* byteStart, int byteCount)
 1269            {
 01270                _enc = enc;
 01271                _decoder = decoder;
 1272
 01273                _chars = charStart;
 01274                _charStart = charStart;
 01275                _charEnd = charStart + charCount;
 1276
 01277                _byteStart = byteStart;
 01278                _bytes = byteStart;
 01279                _byteEnd = byteStart + byteCount;
 1280
 01281                _fallbackBuffer = _decoder is null ?
 01282                    enc.DecoderFallback.CreateFallbackBuffer() :
 01283                    _decoder.FallbackBuffer;
 1284
 1285                // If we're getting chars or getting char count we don't expect to have
 1286                // to remember fallbacks between calls (so it should be empty)
 01287                Debug.Assert(_fallbackBuffer.Remaining == 0,
 01288                    "[Encoding.EncodingCharBuffer.EncodingCharBuffer]Expected empty fallback buffer for getchars/charcou
 01289                _fallbackBuffer.InternalInitialize(_bytes, _charEnd);
 01290            }
 1291
 1292            internal unsafe bool AddChar(char ch, int numBytes)
 1293            {
 01294                if (_chars is not null)
 1295                {
 01296                    if (_chars >= _charEnd)
 1297                    {
 1298                        // Throw maybe
 01299                        _bytes -= numBytes;                                        // Didn't encode these bytes
 01300                        _enc.ThrowCharsOverflow(_decoder, _chars == _charStart);    // Throw?
 01301                        return false;                                           // No throw, but no store either
 1302                    }
 1303
 01304                    *(_chars++) = ch;
 1305                }
 01306                _charCountResult++;
 01307                return true;
 1308            }
 1309
 01310            internal bool AddChar(char ch) => AddChar(ch, 1);
 1311
 1312            internal unsafe bool AddChar(char ch1, char ch2, int numBytes)
 1313            {
 1314                // Need room for 2 chars
 1315                if (_chars >= _charEnd - 1)
 1316                {
 1317                    // Throw maybe
 1318                    _bytes -= numBytes;                                        // Didn't encode these bytes
 1319                    _enc.ThrowCharsOverflow(_decoder, _chars == _charStart);    // Throw?
 1320                    return false;                                           // No throw, but no store either
 1321                }
 1322                return AddChar(ch1, numBytes) && AddChar(ch2, numBytes);
 1323            }
 1324
 1325            internal unsafe void AdjustBytes(int count)
 1326            {
 01327                _bytes += count;
 01328            }
 1329
 01330            internal unsafe bool MoreData => _bytes < _byteEnd;
 1331
 1332            // Do we have count more bytes?
 1333            internal unsafe bool EvenMoreData(int count) => _bytes <= _byteEnd - count;
 1334
 1335            // GetNextByte shouldn't be called unless the caller's already checked more data or even more data,
 1336            // but we'll double check just to make sure.
 1337            internal unsafe byte GetNextByte()
 1338            {
 01339                Debug.Assert(_bytes < _byteEnd, "[EncodingCharBuffer.GetNextByte]Expected more date");
 01340                if (_bytes >= _byteEnd)
 01341                    return 0;
 01342                return *(_bytes++);
 1343            }
 1344
 01345            internal unsafe int BytesUsed => (int)(_bytes - _byteStart);
 1346
 1347            internal bool Fallback(byte fallbackByte)
 1348            {
 1349                // Build our buffer
 01350                byte[] byteBuffer = [fallbackByte];
 1351
 1352                // Do the fallback and add the data.
 01353                return Fallback(byteBuffer);
 1354            }
 1355
 1356            internal bool Fallback(byte byte1, byte byte2)
 1357            {
 1358                // Build our buffer
 1359                byte[] byteBuffer = [byte1, byte2];
 1360
 1361                // Do the fallback and add the data.
 1362                return Fallback(byteBuffer);
 1363            }
 1364
 1365            internal bool Fallback(byte byte1, byte byte2, byte byte3, byte byte4)
 1366            {
 1367                // Build our buffer
 1368                byte[] byteBuffer = [byte1, byte2, byte3, byte4];
 1369
 1370                // Do the fallback and add the data.
 1371                return Fallback(byteBuffer);
 1372            }
 1373
 1374            internal unsafe bool Fallback(byte[] byteBuffer)
 1375            {
 1376                // Do the fallback and add the data.
 01377                if (_chars is not null)
 1378                {
 01379                    char* pTemp = _chars;
 01380                    if (!_fallbackBuffer.InternalFallback(byteBuffer, _bytes, ref _chars))
 1381                    {
 1382                        // Throw maybe
 01383                        _bytes -= byteBuffer.Length;                             // Didn't use how many ever bytes we're
 01384                        _fallbackBuffer.InternalReset();                         // We didn't use this fallback.
 01385                        _enc.ThrowCharsOverflow(_decoder, _chars == _charStart);    // Throw?
 01386                        return false;                                           // No throw, but no store either
 1387                    }
 01388                    _charCountResult += unchecked((int)(_chars - pTemp));
 1389                }
 1390                else
 1391                {
 01392                    _charCountResult += _fallbackBuffer.InternalFallback(byteBuffer, _bytes);
 1393                }
 1394
 01395                return true;
 1396            }
 1397
 01398            internal int Count => _charCountResult;
 1399        }
 1400
 1401        internal sealed class EncodingByteBuffer
 1402        {
 1403            private unsafe byte* _bytes;
 1404            private readonly unsafe byte* _byteStart;
 1405            private readonly unsafe byte* _byteEnd;
 1406            private unsafe char* _chars;
 1407            private readonly unsafe char* _charStart;
 1408            private readonly unsafe char* _charEnd;
 1409            private int _byteCountResult;
 1410            private readonly Encoding _enc;
 1411            private readonly EncoderNLS? _encoder;
 1412            internal EncoderFallbackBuffer fallbackBuffer;
 1413
 01414            internal unsafe EncodingByteBuffer(Encoding inEncoding, EncoderNLS? inEncoder,
 01415                        byte* inByteStart, int inByteCount, char* inCharStart, int inCharCount)
 1416            {
 01417                _enc = inEncoding;
 01418                _encoder = inEncoder;
 1419
 01420                _charStart = inCharStart;
 01421                _chars = inCharStart;
 01422                _charEnd = inCharStart + inCharCount;
 1423
 01424                _bytes = inByteStart;
 01425                _byteStart = inByteStart;
 01426                _byteEnd = inByteStart + inByteCount;
 1427
 01428                if (_encoder is null)
 1429                {
 01430                    this.fallbackBuffer = _enc.EncoderFallback.CreateFallbackBuffer();
 1431                }
 1432                else
 1433                {
 01434                    this.fallbackBuffer = _encoder.FallbackBuffer;
 1435                    // If we're not converting we must not have data in our fallback buffer
 01436                    if (_encoder._throwOnOverflow && _encoder.InternalHasFallbackBuffer &&
 01437                        this.fallbackBuffer.Remaining > 0)
 01438                        throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty,
 01439                            _encoder.Encoding.EncodingName, _encoder.Fallback!.GetType()));
 1440                }
 01441                fallbackBuffer.InternalInitialize(_chars, _charEnd, _encoder, _bytes is not null);
 01442            }
 1443
 1444            internal unsafe bool AddByte(byte b, int moreBytesExpected)
 1445            {
 01446                Debug.Assert(moreBytesExpected >= 0, "[EncodingByteBuffer.AddByte]expected non-negative moreBytesExpecte
 01447                if (_bytes is not null)
 1448                {
 01449                    if (_bytes >= _byteEnd - moreBytesExpected)
 1450                    {
 1451                        // Throw maybe.  Check which buffer to back up (only matters if Converting)
 01452                        this.MovePrevious(true);            // Throw if necessary
 01453                        return false;                       // No throw, but no store either
 1454                    }
 1455
 01456                    *(_bytes++) = b;
 1457                }
 01458                _byteCountResult++;
 01459                return true;
 1460            }
 1461
 01462            internal bool AddByte(byte b1) => AddByte(b1, 0);
 1463
 01464            internal bool AddByte(byte b1, byte b2) => AddByte(b1, b2, 0);
 1465
 1466            internal bool AddByte(byte b1, byte b2, int moreBytesExpected) =>
 01467                AddByte(b1, 1 + moreBytesExpected) && AddByte(b2, moreBytesExpected);
 1468
 1469            internal bool AddByte(byte b1, byte b2, byte b3) =>
 1470                AddByte(b1, b2, b3, (int)0);
 1471
 1472            internal bool AddByte(byte b1, byte b2, byte b3, int moreBytesExpected) =>
 1473                AddByte(b1, 2 + moreBytesExpected) &&
 1474                AddByte(b2, 1 + moreBytesExpected) &&
 1475                AddByte(b3, moreBytesExpected);
 1476
 1477            internal bool AddByte(byte b1, byte b2, byte b3, byte b4) => AddByte(b1, 3) &&
 1478                AddByte(b2, 2) &&
 1479                AddByte(b3, 1) &&
 1480                AddByte(b4, 0);
 1481
 1482            internal unsafe void MovePrevious(bool bThrow)
 1483            {
 01484                if (fallbackBuffer.bFallingBack)
 01485                    fallbackBuffer.MovePrevious();                      // don't use last fallback
 1486                else
 1487                {
 01488                    Debug.Assert(_chars > _charStart ||
 01489                        (bThrow && (_bytes == _byteStart)),
 01490                        "[EncodingByteBuffer.MovePrevious]expected previous data or throw");
 01491                    if (_chars > _charStart)
 01492                        _chars--;                                        // don't use last char
 1493                }
 1494
 01495                if (bThrow)
 01496                    _enc.ThrowBytesOverflow(_encoder, _bytes == _byteStart);    // Throw? (and reset fallback if not con
 01497            }
 1498
 1499            internal unsafe bool Fallback(char charFallback)
 1500            {
 1501                // Do the fallback
 1502                return fallbackBuffer.InternalFallback(charFallback, ref _chars);
 1503            }
 1504
 1505            internal unsafe bool MoreData =>
 1506                // See if fallbackBuffer is not empty or if there's data left in chars buffer.
 01507                (fallbackBuffer.Remaining > 0) || (_chars < _charEnd);
 1508
 1509            internal unsafe char GetNextChar()
 1510            {
 1511                // See if there's something in our fallback buffer
 01512                char cReturn = fallbackBuffer.InternalGetNextChar();
 1513
 1514                // Nothing in the fallback buffer, return our normal data.
 01515                if (cReturn == 0)
 1516                {
 01517                    if (_chars < _charEnd)
 01518                        cReturn = *(_chars++);
 1519                }
 1520
 01521                return cReturn;
 1522            }
 1523
 01524            internal unsafe int CharsUsed => (int)(_chars - _charStart);
 1525
 01526            internal int Count => _byteCountResult;
 1527        }
 1528    }
 1529}
 1530

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.Internal.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
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9
 10namespace System.Text
 11{
 12    public partial class Encoding
 13    {
 14        /*
 15         * This file contains infrastructure code that supports a simplified way of writing
 16         * internally-implemented Encoding types. In this system, the individual Encoding types
 17         * are no longer responsible for handling anything related to the EncoderNLS / DecoderNLS
 18         * infrastructure, nor are they responsible for implementing anything related to fallback
 19         * buffers logic.
 20         *
 21         * Instead, subclassed types are responsible only for transcoding of individual scalar values
 22         * to and from the encoding's byte representation (see the two methods immediately below).
 23         * They can optionally implement fast-path logic to perform bulk transcoding up until the
 24         * first segment of data that cannot be transcoded. They can special-case certain fallback
 25         * mechanisms if desired.
 26         *
 27         * Most of the fast-path code is written using raw pointers as the exchange types, just as
 28         * in the standard Encoding infrastructure. Since the fallback logic is more complex, most
 29         * of it is written using type-safe constructs like Span<T>, with some amount of glue to
 30         * allow it to work correctly with pointer-based fast-path code.
 31         *
 32         * A typical call graph for GetBytes is represented below, using ASCIIEncoding as an example.
 33         *
 34         * ASCIIEncoding.GetBytes(...) [non-EncoderNLS path, public virtual override]
 35         * `- <parameter validation>
 36         *  - ASCIIEncoding.GetBytesCommon [private helper method per derived type, inlined]
 37         *    `- ASCIIEncoding.GetBytesFast [overridden fast-path implementation, inlined]
 38         *     - <if all data transcoded, return immediately>
 39         *     - <if all data not transcoded...>
 40         *       `- Encoding.GetBytesWithFallback [non-virtual stub method to call main GetBytesWithFallback worker]
 41         *          `- Encoding.GetBytesWithFallback [virtual method whose base implementation contains slow fallback lo
 42         *             `- <may be overridden to provide optimized fallback logic>
 43         *              - <create EncodeFallbackBuffer instance>
 44         *              - <perform the following in a loop:>
 45         *                `- <invoke fast-path logic via virtual method dispatch on derived type>
 46         *                 - <read next "bad" scalar value from source>
 47         *                 - <run this bad value through the fallback buffer>
 48         *                 - <drain the fallback buffer to the destination>
 49         *                 - <loop until source is fully consumed or destination is full>
 50         *              - <signal full or partial success to EncoderNLS instance / throw if necessary>
 51         *
 52         * The call graph for GetBytes(..., EncoderNLS) is similar:
 53         *
 54         * Encoding.GetBytes(..., EncoderNLS) [base implementation]
 55         * `- <if no leftover data from previous invocation, invoke fast-path>
 56         *  - <if fast-path invocation above completed, return immediately>
 57         *  - <if not all data transcoded, or if there was leftover data from previous invocation...>
 58         *    `- Encoding.GetBytesWithFallback [non-virtual stub method]
 59         *       `- <drain any leftover data from previous invocation>
 60         *        - <invoke fast-path again>
 61         *        - <if all data transcoded, return immediately>
 62         *        - <if all data not transcoded...>
 63         *          `- Encoding.GetBytesWithFallback [virtual method as described above]
 64         *
 65         * There are different considerations in each call graph for things like error handling,
 66         * since the error conditions will be different depending on whether or not an EncoderNLS
 67         * instance is available and what values its properties have.
 68         */
 69
 70        /*
 71         * THESE TWO METHODS MUST BE OVERRIDDEN BY A SUBCLASSED TYPE
 72         */
 73
 74        internal virtual OperationStatus DecodeFirstRune(ReadOnlySpan<byte> bytes, out Rune value, out int bytesConsumed
 75        {
 076            Debug.Fail("This should be overridden by a subclassed type.");
 77            throw NotImplemented.ByDesign;
 78        }
 79
 80        internal virtual OperationStatus EncodeRune(Rune value, Span<byte> bytes, out int bytesWritten)
 81        {
 082            Debug.Fail("This should be overridden by a subclassed type.");
 83            throw NotImplemented.ByDesign;
 84        }
 85
 86        /*
 87         * ALL OTHER LOGIC CAN BE IMPLEMENTED IN TERMS OF THE TWO METHODS ABOVE.
 88         * FOR IMPROVED PERFORMANCE, SUBCLASSED TYPES MAY WANT TO OVERRIDE ONE OR MORE VIRTUAL METHODS BELOW.
 89         */
 90
 91        /*
 92         * GETBYTECOUNT FAMILY OF FUNCTIONS
 93         */
 94
 95        /// <summary>
 96        /// Given a <see cref="Rune"/>, determines its byte count under the current <see cref="Encoding"/>.
 97        /// Returns <see langword="false"/> if the <see cref="Rune"/> cannot be represented in the
 98        /// current <see cref="Encoding"/>.
 99        /// </summary>
 100        internal virtual bool TryGetByteCount(Rune value, out int byteCount)
 101        {
 102            // Any production-quality type would override this method and provide a real
 103            // implementation, so we won't provide a base implementation. However, a
 104            // non-shipping slow reference implementation is provided below for convenience.
 105
 106#if false
 107            Span<byte> bytes = [0, 0, 0, 0]; // max 4 bytes per input scalar
 108
 109            OperationStatus opStatus = EncodeRune(value, bytes, out byteCount);
 110            Debug.Assert(opStatus == OperationStatus.Done || opStatus == OperationStatus.InvalidData, "Unexpected return
 111
 112            return (opStatus == OperationStatus.Done);
 113#else
 0114            Debug.Fail("This should be overridden by a subclassed type.");
 115            throw NotImplemented.ByDesign;
 116#endif
 117        }
 118
 119        /// <summary>
 120        /// Entry point from <see cref="EncoderNLS.GetByteCount"/>.
 121        /// </summary>
 122        internal virtual unsafe int GetByteCount(char* pChars, int charCount, EncoderNLS? encoder)
 123        {
 0124            Debug.Assert(encoder != null, "This code path should only be called from EncoderNLS.");
 0125            Debug.Assert(charCount >= 0, "Caller should've checked this condition.");
 0126            Debug.Assert(pChars != null || charCount == 0, "Cannot provide a null pointer and a non-zero count.");
 127
 128            // We're going to try to stay on the fast-path as much as we can. That means that we have
 129            // no leftover data to drain and the entire source buffer can be consumed in a single
 130            // fast-path invocation. If either of these doesn't hold, we'll go down the slow path of
 131            // creating spans, draining the EncoderNLS instance, and falling back.
 132
 0133            int totalByteCount = 0;
 0134            int charsConsumed = 0;
 135
 0136            if (!encoder.HasLeftoverData)
 137            {
 0138                totalByteCount = GetByteCountFast(pChars, charCount, encoder.Fallback, out charsConsumed);
 0139                if (charsConsumed == charCount)
 140                {
 0141                    return totalByteCount;
 142                }
 143            }
 144
 145            // We had leftover data, or we couldn't consume the entire input buffer.
 146            // Let's go down the draining + fallback mechanisms.
 147
 0148            totalByteCount += GetByteCountWithFallback(pChars, charCount, charsConsumed, encoder);
 0149            if (totalByteCount < 0)
 150            {
 0151                ThrowConversionOverflow();
 152            }
 153
 0154            return totalByteCount;
 155        }
 156
 157        /// <summary>
 158        /// Counts the number of <see langword="byte"/>s that would result from transcoding the source
 159        /// data, exiting when the source buffer is consumed or when the first unreadable data is encountered.
 160        /// The implementation may inspect <paramref name="fallback"/> to short-circuit any counting
 161        /// operation, but it should not attempt to call <see cref="EncoderFallback.CreateFallbackBuffer"/>.
 162        /// </summary>
 163        /// <returns>
 164        /// Via <paramref name="charsConsumed"/>, the number of elements from <paramref name="pChars"/> which
 165        /// were consumed; and returns the transcoded byte count up to this point.
 166        /// </returns>
 167        /// <exception cref="ArgumentException">
 168        /// If the byte count would be greater than <see cref="int.MaxValue"/>.
 169        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 170        /// </exception>
 171        /// <remarks>
 172        /// The implementation should not attempt to perform any sort of fallback behavior.
 173        /// If custom fallback behavior is necessary, override <see cref="GetByteCountWithFallback"/>.
 174        /// </remarks>
 175        private protected virtual unsafe int GetByteCountFast(char* pChars, int charsLength, EncoderFallback? fallback, 
 176        {
 177            // Any production-quality type would override this method and provide a real
 178            // implementation, so we won't provide a base implementation. However, a
 179            // non-shipping slow reference implementation is provided below for convenience.
 180
 181#if false
 182            ReadOnlySpan<char> chars = new ReadOnlySpan<char>(pChars, charsLength);
 183            int totalByteCount = 0;
 184
 185            while (!chars.IsEmpty)
 186            {
 187                if (Rune.DecodeUtf16(chars, out Rune scalarValue, out int charsConsumedThisIteration) != OperationStatus
 188                    || !TryGetByteCount(scalarValue, out int byteCountThisIteration))
 189                {
 190                    // Invalid UTF-16 data, or not convertible to target encoding
 191
 192                    break;
 193                }
 194
 195                chars = chars.Slice(charsConsumedThisIteration);
 196
 197                totalByteCount += byteCountThisIteration;
 198                if (totalByteCount < 0)
 199                {
 200                    ThrowConversionOverflow();
 201                }
 202            }
 203
 204            charsConsumed = charsLength - chars.Length; // number of chars consumed across all loop iterations above
 205            return totalByteCount;
 206#else
 0207            Debug.Fail("This should be overridden by a subclassed type.");
 208            throw NotImplemented.ByDesign;
 209#endif
 210        }
 211
 212        /// <summary>
 213        /// Counts the number of bytes that would result from transcoding the provided chars,
 214        /// with no associated <see cref="EncoderNLS"/>. The first two arguments are based on the
 215        /// original input before invoking this method; and <paramref name="charsConsumedSoFar"/>
 216        /// signals where in the provided buffer the fallback loop should begin operating.
 217        /// </summary>
 218        /// <returns>
 219        /// The byte count resulting from transcoding the input data.
 220        /// </returns>
 221        /// <exception cref="ArgumentException">
 222        /// If the resulting byte count is greater than <see cref="int.MaxValue"/>.
 223        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 224        /// </exception>
 225        [MethodImpl(MethodImplOptions.NoInlining)] // don't stack spill spans into our caller
 226        private protected unsafe int GetByteCountWithFallback(char* pCharsOriginal, int originalCharCount, int charsCons
 227        {
 228            // This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
 229            // into our immediate caller. Doing so increases the method prolog in what's supposed to
 230            // be a very fast path.
 231
 0232            Debug.Assert(0 <= charsConsumedSoFar && charsConsumedSoFar < originalCharCount, "Invalid arguments provided 
 233
 0234            return GetByteCountWithFallback(
 0235                chars: new ReadOnlySpan<char>(pCharsOriginal, originalCharCount).Slice(charsConsumedSoFar),
 0236                originalCharsLength: originalCharCount,
 0237                encoder: null);
 238        }
 239
 240        /// <summary>
 241        /// Gets the number of <see langword="byte"/>s that would result from transcoding the provided
 242        /// input data, with an associated <see cref="EncoderNLS"/>. The first two arguments are
 243        /// based on the original input before invoking this method; and <paramref name="charsConsumedSoFar"/>
 244        /// signals where in the provided source buffer the fallback loop should begin operating.
 245        /// The behavior of this method is to consume (non-destructively) any leftover data in the
 246        /// <see cref="EncoderNLS"/> instance, then to invoke the <see cref="GetByteCountFast"/> virtual method
 247        /// after data has been drained, then to call <see cref="GetByteCountWithFallback(ReadOnlySpan{char}, int, Encod
 248        /// </summary>
 249        /// <returns>
 250        /// The total number of bytes that would result from transcoding the remaining portion of the source buffer.
 251        /// </returns>
 252        /// <exception cref="ArgumentException">
 253        /// If the return value would exceed <see cref="int.MaxValue"/>.
 254        /// (The implementation should call <see cref="ThrowConversionOverflow"/>.)
 255        /// </exception>
 256        private unsafe int GetByteCountWithFallback(char* pOriginalChars, int originalCharCount, int charsConsumedSoFar,
 257        {
 0258            Debug.Assert(encoder != null, "This code path should only be called from EncoderNLS.");
 0259            Debug.Assert(0 <= charsConsumedSoFar && charsConsumedSoFar <= originalCharCount, "Caller should've checked t
 260
 261            // First, try draining any data that already exists on the encoder instance. If we can't complete
 262            // that operation, there's no point to continuing down to the main workhorse methods.
 263
 0264            ReadOnlySpan<char> chars = new ReadOnlySpan<char>(pOriginalChars, originalCharCount).Slice(charsConsumedSoFa
 265
 0266            int totalByteCount = encoder.DrainLeftoverDataForGetByteCount(chars, out int charsConsumedJustNow);
 0267            chars = chars.Slice(charsConsumedJustNow);
 268
 269            // Now try invoking the "fast path" (no fallback) implementation.
 270            // We can use Unsafe.AsPointer here since these spans are created from pinned data (raw pointers).
 271
 0272            totalByteCount += GetByteCountFast(
 0273                pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 0274                charsLength: chars.Length,
 0275                fallback: encoder.Fallback,
 0276                charsConsumed: out charsConsumedJustNow);
 277
 0278            if (totalByteCount < 0)
 279            {
 0280                ThrowConversionOverflow();
 281            }
 282
 0283            chars = chars.Slice(charsConsumedJustNow);
 284
 285            // If there's still data remaining in the source buffer, go down the fallback path.
 286            // Otherwise we're finished.
 287
 0288            if (!chars.IsEmpty)
 289            {
 0290                totalByteCount += GetByteCountWithFallback(chars, originalCharCount, encoder);
 0291                if (totalByteCount < 0)
 292                {
 0293                    ThrowConversionOverflow();
 294                }
 295            }
 296
 0297            return totalByteCount;
 298        }
 299
 300        /// <summary>
 301        /// Counts the number of bytes that would result from transcoding the provided chars,
 302        /// using the provided <see cref="EncoderFallbackBuffer"/> if necessary.
 303        /// </summary>
 304        /// <returns>
 305        /// The byte count resulting from transcoding the input data.
 306        /// </returns>
 307        /// <exception cref="ArgumentException">
 308        /// If the resulting byte count is greater than <see cref="int.MaxValue"/>.
 309        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 310        /// </exception>
 311        private protected virtual unsafe int GetByteCountWithFallback(ReadOnlySpan<char> chars, int originalCharsLength,
 312        {
 0313            Debug.Assert(!chars.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
 0314            Debug.Assert(originalCharsLength >= 0, "Caller provided invalid parameter.");
 315
 316            // Since we're using Unsafe.AsPointer in our central loop, we want to ensure everything is pinned.
 317
 0318            fixed (char* _pChars_Unused = &MemoryMarshal.GetReference(chars))
 319            {
 0320                EncoderFallbackBuffer fallbackBuffer = EncoderFallbackBuffer.CreateAndInitialize(this, encoder, original
 0321                int totalByteCount = 0;
 322
 323                do
 324                {
 325                    // There's still data in the source buffer; why wasn't the previous fast-path able to consume it ful
 326                    // There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
 327                    // or (b) the encoding can't translate this scalar value.
 328
 0329                    if (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration) == Op
 0330                           && encoder != null
 0331                           && !encoder.MustFlush)
 332                    {
 333                        // We saw a standalone high surrogate at the end of the buffer, and the
 334                        // active EncoderNLS instance isn't asking us to flush. Since a call to
 335                        // GetBytes would've consumed this char by storing it in EncoderNLS._charLeftOver,
 336                        // we'll "consume" it by ignoring it. The next call to GetBytes will
 337                        // pick it up correctly.
 338
 339                        goto Finish;
 340                    }
 341
 342                    // We saw invalid UTF-16 data, or we saw a high surrogate that we need to flush (and
 343                    // thus treat as invalid), or we saw valid UTF-16 data that this encoder doesn't support.
 344                    // In any case we'll run it through the fallback mechanism.
 345
 0346                    int byteCountThisIteration = fallbackBuffer.InternalFallbackGetByteCount(chars, out charsConsumedThi
 347
 0348                    Debug.Assert(byteCountThisIteration >= 0, "Fallback shouldn't have returned a negative value.");
 0349                    Debug.Assert(charsConsumedThisIteration >= 0, "Fallback shouldn't have returned a negative value.");
 350
 0351                    totalByteCount += byteCountThisIteration;
 0352                    if (totalByteCount < 0)
 353                    {
 0354                        ThrowConversionOverflow();
 355                    }
 356
 0357                    chars = chars.Slice(charsConsumedThisIteration);
 358
 0359                    if (!chars.IsEmpty)
 360                    {
 361                        // Still data remaining - run it through the fast-path to find the next data to fallback.
 362                        // While building up the tally we need to continually check for integer overflow
 363                        // since fallbacks can change the total byte count in unexpected ways.
 364
 0365                        byteCountThisIteration = GetByteCountFast(
 0366                            pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 0367                            charsLength: chars.Length,
 0368                            fallback: null, // already tried this earlier and we still fell down the common path, so ski
 0369                            charsConsumed: out charsConsumedThisIteration);
 370
 0371                        Debug.Assert(byteCountThisIteration >= 0, "Workhorse shouldn't have returned a negative value.")
 0372                        Debug.Assert(charsConsumedThisIteration >= 0, "Workhorse shouldn't have returned a negative valu
 373
 0374                        totalByteCount += byteCountThisIteration;
 0375                        if (totalByteCount < 0)
 376                        {
 0377                            ThrowConversionOverflow();
 378                        }
 379
 0380                        chars = chars.Slice(charsConsumedThisIteration);
 381                    }
 0382                } while (!chars.IsEmpty);
 383
 384            Finish:
 385
 0386                Debug.Assert(fallbackBuffer.Remaining == 0, "There should be no data in the fallback buffer after GetByt
 387
 0388                return totalByteCount;
 389            }
 390        }
 391
 392        /*
 393         * GETBYTES FAMILY OF FUNCTIONS
 394         */
 395
 396        /// <summary>
 397        /// Entry point from <see cref="EncoderNLS.GetBytes"/> and <see cref="EncoderNLS.Convert"/>.
 398        /// </summary>
 399        internal virtual unsafe int GetBytes(char* pChars, int charCount, byte* pBytes, int byteCount, EncoderNLS? encod
 400        {
 3401            Debug.Assert(encoder != null, "This code path should only be called from EncoderNLS.");
 3402            Debug.Assert(charCount >= 0, "Caller should've checked this condition.");
 3403            Debug.Assert(pChars != null || charCount == 0, "Cannot provide a null pointer and a non-zero count.");
 3404            Debug.Assert(byteCount >= 0, "Caller should've checked this condition.");
 3405            Debug.Assert(pBytes != null || byteCount == 0, "Cannot provide a null pointer and a non-zero count.");
 406
 407            // We're going to try to stay on the fast-path as much as we can. That means that we have
 408            // no leftover data to drain and the entire source buffer can be transcoded in a single
 409            // fast-path invocation. If either of these doesn't hold, we'll go down the slow path of
 410            // creating spans, draining the EncoderNLS instance, and falling back.
 411
 3412            int bytesWritten = 0;
 3413            int charsConsumed = 0;
 414
 3415            if (!encoder.HasLeftoverData)
 416            {
 3417                bytesWritten = GetBytesFast(pChars, charCount, pBytes, byteCount, out charsConsumed);
 3418                if (charsConsumed == charCount)
 419                {
 3420                    encoder._charsUsed = charCount;
 3421                    return bytesWritten;
 422                }
 423            }
 424
 425            // We had leftover data, or we couldn't consume the entire input buffer.
 426            // Let's go down the draining + fallback mechanisms.
 427
 0428            return GetBytesWithFallback(pChars, charCount, pBytes, byteCount, charsConsumed, bytesWritten, encoder);
 429        }
 430
 431        /// <summary>
 432        /// Transcodes <see langword="char"/>s to <see langword="byte"/>s, exiting when the source or destination
 433        /// buffer is consumed or when the first unreadable data is encountered.
 434        /// </summary>
 435        /// <returns>
 436        /// Via <paramref name="charsConsumed"/>, the number of elements from <paramref name="pChars"/> which
 437        /// were consumed; and returns the number of elements written to <paramref name="pBytes"/>.
 438        /// </returns>
 439        /// <remarks>
 440        /// The implementation should not attempt to perform any sort of fallback behavior.
 441        /// If custom fallback behavior is necessary, override <see cref="GetBytesWithFallback"/>.
 442        /// </remarks>
 443        private protected virtual unsafe int GetBytesFast(char* pChars, int charsLength, byte* pBytes, int bytesLength, 
 444        {
 445            // Any production-quality type would override this method and provide a real
 446            // implementation, so we won't provide a base implementation. However, a
 447            // non-shipping slow reference implementation is provided below for convenience.
 448
 449#if false
 450            ReadOnlySpan<char> chars = new ReadOnlySpan<char>(pChars, charsLength);
 451            Span<byte> bytes = new Span<byte>(pBytes, bytesLength);
 452
 453            while (!chars.IsEmpty)
 454            {
 455                if (Rune.DecodeUtf16(chars, out Rune scalarValue, out int charsConsumedJustNow) != OperationStatus.Done
 456                    || EncodeRune(scalarValue, bytes, out int bytesWrittenJustNow) != OperationStatus.Done)
 457                {
 458                    // Invalid UTF-16 data, or not convertible to target encoding, or destination buffer too small to co
 459
 460                    break;
 461                }
 462
 463                chars = chars.Slice(charsConsumedJustNow);
 464                bytes = bytes.Slice(bytesWrittenJustNow);
 465            }
 466
 467            charsConsumed = charsLength - chars.Length; // number of chars consumed across all loop iterations above
 468            return bytesLength - bytes.Length; // number of bytes written across all loop iterations above
 469#else
 0470            Debug.Fail("This should be overridden by a subclassed type.");
 471            throw NotImplemented.ByDesign;
 472#endif
 473        }
 474
 475        /// <summary>
 476        /// Transcodes chars to bytes, with no associated <see cref="EncoderNLS"/>. The first four arguments are
 477        /// based on the original input before invoking this method; and <paramref name="charsConsumedSoFar"/>
 478        /// and <paramref name="bytesWrittenSoFar"/> signal where in the provided buffers the fallback loop
 479        /// should begin operating. The behavior of this method is to call the <see cref="GetBytesWithFallback"/>
 480        /// virtual method as overridden by the specific type, and failing that go down the shared fallback path.
 481        /// </summary>
 482        /// <returns>
 483        /// The total number of bytes written to <paramref name="pOriginalBytes"/>, including <paramref name="bytesWritt
 484        /// </returns>
 485        /// <exception cref="ArgumentException">
 486        /// If the destination buffer is not large enough to hold the entirety of the transcoded data.
 487        /// </exception>
 488        [MethodImpl(MethodImplOptions.NoInlining)]
 489        private protected unsafe int GetBytesWithFallback(char* pOriginalChars, int originalCharCount, byte* pOriginalBy
 490        {
 491            // This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
 492            // into our immediate caller. Doing so increases the method prolog in what's supposed to
 493            // be a very fast path.
 494
 0495            Debug.Assert(0 <= charsConsumedSoFar && charsConsumedSoFar < originalCharCount, "Invalid arguments provided 
 0496            Debug.Assert(0 <= bytesWrittenSoFar && bytesWrittenSoFar <= originalByteCount, "Invalid arguments provided t
 497
 0498            return GetBytesWithFallback(
 0499                chars: new ReadOnlySpan<char>(pOriginalChars, originalCharCount).Slice(charsConsumedSoFar),
 0500                originalCharsLength: originalCharCount,
 0501                bytes: new Span<byte>(pOriginalBytes, originalByteCount).Slice(bytesWrittenSoFar),
 0502                originalBytesLength: originalByteCount,
 0503                encoder: null,
 0504                throwForDestinationOverflow);
 505        }
 506
 507        /// <summary>
 508        /// Transcodes chars to bytes, with an associated <see cref="EncoderNLS"/>. The first four arguments are
 509        /// based on the original input before invoking this method; and <paramref name="charsConsumedSoFar"/>
 510        /// and <paramref name="bytesWrittenSoFar"/> signal where in the provided buffers the fallback loop
 511        /// should begin operating. The behavior of this method is to drain any leftover data in the
 512        /// <see cref="EncoderNLS"/> instance, then to invoke the <see cref="GetBytesFast"/> virtual method
 513        /// after data has been drained, then to call <see cref="GetBytesWithFallback(ReadOnlySpan{char}, int, Span{byte
 514        /// </summary>
 515        /// <returns>
 516        /// The total number of bytes written to <paramref name="pOriginalBytes"/>, including <paramref name="bytesWritt
 517        /// </returns>
 518        /// <exception cref="ArgumentException">
 519        /// If the destination buffer is too small to make any forward progress at all, or if the destination buffer is
 520        /// too small to contain the entirety of the transcoded data and the <see cref="EncoderNLS"/> instance disallows
 521        /// partial transcoding.
 522        /// </exception>
 523        private unsafe int GetBytesWithFallback(char* pOriginalChars, int originalCharCount, byte* pOriginalBytes, int o
 524        {
 0525            Debug.Assert(encoder != null, "This code path should only be called from EncoderNLS.");
 0526            Debug.Assert(0 <= charsConsumedSoFar && charsConsumedSoFar <= originalCharCount, "Caller should've checked t
 0527            Debug.Assert(0 <= bytesWrittenSoFar && bytesWrittenSoFar <= originalByteCount, "Caller should've checked thi
 528
 529            // First, try draining any data that already exists on the encoder instance. If we can't complete
 530            // that operation, there's no point to continuing down to the main workhorse methods.
 531
 0532            ReadOnlySpan<char> chars = new ReadOnlySpan<char>(pOriginalChars, originalCharCount).Slice(charsConsumedSoFa
 0533            Span<byte> bytes = new Span<byte>(pOriginalBytes, originalByteCount).Slice(bytesWrittenSoFar);
 534
 0535            bool drainFinishedSuccessfully = encoder.TryDrainLeftoverDataForGetBytes(chars, bytes, out int charsConsumed
 536
 0537            chars = chars.Slice(charsConsumedJustNow); // whether or not the drain finished, we may have made some progr
 0538            bytes = bytes.Slice(bytesWrittenJustNow);
 539
 0540            if (!drainFinishedSuccessfully)
 541            {
 0542                ThrowBytesOverflow(encoder, nothingEncoded: bytes.Length == originalByteCount); // might not throw if we
 543            }
 544            else
 545            {
 546                // Now try invoking the "fast path" (no fallback) implementation.
 547                // We can use Unsafe.AsPointer here since these spans are created from pinned data (raw pointers).
 548
 0549                bytesWrittenJustNow = GetBytesFast(
 0550                    pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 0551                    charsLength: chars.Length,
 0552                    pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 0553                    bytesLength: bytes.Length,
 0554                    charsConsumed: out charsConsumedJustNow);
 555
 0556                chars = chars.Slice(charsConsumedJustNow);
 0557                bytes = bytes.Slice(bytesWrittenJustNow);
 558
 559                // If there's still data remaining in the source buffer, go down the fallback path.
 560                // Otherwise we're finished.
 561
 0562                if (!chars.IsEmpty)
 563                {
 564                    // We'll optimistically tell the encoder that we're using everything; the
 565                    // GetBytesWithFallback method will overwrite this field if necessary.
 566
 0567                    encoder._charsUsed = originalCharCount;
 0568                    return GetBytesWithFallback(chars, originalCharCount, bytes, originalByteCount, encoder);
 569                }
 570            }
 571
 0572            encoder._charsUsed = originalCharCount - chars.Length; // total number of characters consumed up until now
 0573            return originalByteCount - bytes.Length; // total number of bytes written up until now
 574        }
 575
 576        /// <summary>
 577        /// Transcodes chars to bytes, using <see cref="EncoderFallback"/> or <see cref="Encoder.Fallback"/> if needed.
 578        /// </summary>
 579        /// <returns>
 580        /// The total number of bytes written to <paramref name="bytes"/> (based on <paramref name="originalBytesLength"
 581        /// </returns>
 582        /// <remarks>
 583        /// The derived class should override this method if it might be able to provide a more optimized fallback
 584        /// implementation, deferring to the base implementation if needed. This method calls <see cref="ThrowBytesOverf
 585        /// if necessary.
 586        /// </remarks>
 587        private protected virtual unsafe int GetBytesWithFallback(ReadOnlySpan<char> chars, int originalCharsLength, Spa
 588        {
 0589            Debug.Assert(!chars.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
 0590            Debug.Assert(originalCharsLength >= 0, "Caller provided invalid parameter.");
 0591            Debug.Assert(originalBytesLength >= 0, "Caller provided invalid parameter.");
 592
 593            // Since we're using Unsafe.AsPointer in our central loop, we want to ensure everything is pinned.
 594
 0595            fixed (char* _pChars_Unused = &MemoryMarshal.GetReference(chars))
 0596            fixed (byte* _pBytes_Unused = &MemoryMarshal.GetReference(bytes))
 597            {
 0598                EncoderFallbackBuffer fallbackBuffer = EncoderFallbackBuffer.CreateAndInitialize(this, encoder, original
 599
 600                do
 601                {
 602                    // There's still data in the source buffer; why wasn't the previous fast-path able to consume it ful
 603                    // There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
 604                    // or (b) the encoding can't translate this scalar value.
 605
 0606                    switch (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration))
 607                    {
 608                        case OperationStatus.NeedMoreData:
 0609                            Debug.Assert(charsConsumedThisIteration == chars.Length, "If returning NeedMoreData, should 
 0610                            if (encoder is null || encoder.MustFlush)
 611                            {
 612                                goto case OperationStatus.InvalidData; // see comment in GetByteCountWithFallback
 613                            }
 614                            else
 615                            {
 0616                                encoder._charLeftOver = chars[0]; // squirrel away remaining high surrogate char and fin
 0617                                chars = ReadOnlySpan<char>.Empty;
 0618                                goto Finish;
 619                            }
 620
 621                        case OperationStatus.InvalidData:
 622                            break;
 623
 624                        default:
 0625                            if (EncodeRune(firstScalarValue, bytes, out _) == OperationStatus.DestinationTooSmall)
 626                            {
 627                                goto Finish; // source buffer contained valid UTF-16 but encoder ran out of space in des
 628                            }
 629                            break; // source buffer contained valid UTF-16 but encoder doesn't support this scalar value
 630                    }
 631
 632                    // Now we know the reason for failure was that the original input was invalid
 633                    // for the encoding in use. Run it through the fallback mechanism.
 634
 0635                    bool fallbackFinished = fallbackBuffer.TryInternalFallbackGetBytes(chars, bytes, out charsConsumedTh
 636
 637                    // Regardless of whether the fallback finished, it did consume some number of
 638                    // chars, and it may have written some number of bytes.
 639
 0640                    chars = chars.Slice(charsConsumedThisIteration);
 0641                    bytes = bytes.Slice(bytesWrittenThisIteration);
 642
 0643                    if (!fallbackFinished)
 644                    {
 645                        goto Finish; // fallback has pending state - it'll get written out on the next GetBytes call
 646                    }
 647
 0648                    if (!chars.IsEmpty)
 649                    {
 650                        // Still data remaining - run it through the fast-path to find the next data to fallback.
 651
 0652                        bytesWrittenThisIteration = GetBytesFast(
 0653                            pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 0654                            charsLength: chars.Length,
 0655                            pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 0656                            bytesLength: bytes.Length,
 0657                            charsConsumed: out charsConsumedThisIteration);
 658
 0659                        Debug.Assert(bytesWrittenThisIteration >= 0, "Workhorse shouldn't have returned a negative value
 0660                        Debug.Assert(charsConsumedThisIteration >= 0, "Workhorse shouldn't have returned a negative valu
 661
 0662                        chars = chars.Slice(charsConsumedThisIteration);
 0663                        bytes = bytes.Slice(bytesWrittenThisIteration);
 664                    }
 0665                } while (!chars.IsEmpty);
 666
 667            Finish:
 668
 669                // We reach this point when we deplete the source or destination buffer. There are a few
 670                // cases to consider now. If the source buffer has been fully consumed and there's no
 671                // leftover data in the EncoderNLS or the fallback buffer, we've completed transcoding.
 672                // If the source buffer isn't empty or there's leftover data in the fallback buffer,
 673                // it means we ran out of space in the destintion buffer. This is an unrecoverable error
 674                // if no EncoderNLS is in use (because only EncoderNLS can handle partial success), and
 675                // even if an EncoderNLS is in use this is only recoverable if the EncoderNLS instance
 676                // allows partial completion. Let's check all of these conditions now.
 677
 0678                if (!chars.IsEmpty || fallbackBuffer.Remaining > 0)
 679                {
 680                    // The line below will also throw if the encoder couldn't make any progress at all
 681                    // because the output buffer wasn't large enough to contain the result of even
 682                    // a single scalar conversion or fallback.
 0683                    if (throwForDestinationOverflow)
 684                    {
 0685                        ThrowBytesOverflow(encoder, nothingEncoded: bytes.Length == originalBytesLength);
 686                    }
 687                    else
 688                    {
 0689                        Debug.Assert(encoder is null);
 0690                        return -1;
 691                    }
 692                }
 693
 694                // If an EncoderNLS instance is active, update its "total consumed character count" value.
 695
 0696                if (encoder != null)
 697                {
 0698                    Debug.Assert(originalCharsLength >= chars.Length, "About to report a negative number of chars used?"
 0699                    encoder._charsUsed = originalCharsLength - chars.Length; // number of chars consumed
 700                }
 701
 0702                Debug.Assert(fallbackBuffer.Remaining == 0 || encoder != null, "Shouldn't have any leftover data in fall
 703
 0704                return originalBytesLength - bytes.Length;
 705            }
 706        }
 707
 708        /*
 709         * GETCHARCOUNT FAMILY OF FUNCTIONS
 710         */
 711
 712        /// <summary>
 713        /// Entry point from <see cref="DecoderNLS.GetCharCount"/>.
 714        /// </summary>
 715        internal virtual unsafe int GetCharCount(byte* pBytes, int byteCount, DecoderNLS? decoder)
 716        {
 0717            Debug.Assert(decoder != null, "This code path should only be called from DecoderNLS.");
 0718            Debug.Assert(byteCount >= 0, "Caller should've checked this condition.");
 0719            Debug.Assert(pBytes != null || byteCount == 0, "Cannot provide a null pointer and a non-zero count.");
 720
 721            // We're going to try to stay on the fast-path as much as we can. That means that we have
 722            // no leftover data to drain and the entire source buffer can be consumed in a single
 723            // fast-path invocation. If either of these doesn't hold, we'll go down the slow path of
 724            // creating spans, draining the DecoderNLS instance, and falling back.
 725
 0726            Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "Fallback buffer c
 727
 0728            int totalCharCount = 0;
 0729            int bytesConsumed = 0;
 730
 0731            if (!decoder.HasLeftoverData)
 732            {
 0733                totalCharCount = GetCharCountFast(pBytes, byteCount, decoder.Fallback, out bytesConsumed);
 0734                if (bytesConsumed == byteCount)
 735                {
 0736                    return totalCharCount;
 737                }
 738            }
 739
 740            // We had leftover data, or we couldn't consume the entire input buffer.
 741            // Let's go down the draining + fallback mechanisms.
 742
 0743            totalCharCount += GetCharCountWithFallback(pBytes, byteCount, bytesConsumed, decoder);
 0744            if (totalCharCount < 0)
 745            {
 0746                ThrowConversionOverflow();
 747            }
 748
 0749            return totalCharCount;
 750        }
 751
 752        /// <summary>
 753        /// Counts the number of <see langword="char"/>s that would result from transcoding the source
 754        /// data, exiting when the source buffer is consumed or when the first unreadable data is encountered.
 755        /// The implementation may inspect <paramref name="fallback"/> to short-circuit any counting
 756        /// operation, but it should not attempt to call <see cref="DecoderFallback.CreateFallbackBuffer"/>.
 757        /// </summary>
 758        /// <returns>
 759        /// Via <paramref name="bytesConsumed"/>, the number of elements from <paramref name="pBytes"/> which
 760        /// were consumed; and returns the transcoded char count up to this point.
 761        /// </returns>
 762        /// <exception cref="ArgumentException">
 763        /// If the char count would be greater than <see cref="int.MaxValue"/>.
 764        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 765        /// </exception>
 766        /// <remarks>
 767        /// The implementation should not attempt to perform any sort of fallback behavior.
 768        /// If custom fallback behavior is necessary, override <see cref="GetCharCountWithFallback"/>.
 769        /// </remarks>
 770        private protected virtual unsafe int GetCharCountFast(byte* pBytes, int bytesLength, DecoderFallback? fallback, 
 771        {
 772            // Any production-quality type would override this method and provide a real
 773            // implementation, so we won't provide a base implementation. However, a
 774            // non-shipping slow reference implementation is provided below for convenience.
 775
 776#if false
 777            ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(pBytes, bytesLength);
 778            int totalCharCount = 0;
 779
 780            while (!bytes.IsEmpty)
 781            {
 782                // We don't care about statuses other than Done. The fallback mechanism will handle those.
 783
 784                if (DecodeFirstRune(bytes, out Rune value, out int bytesConsumedJustNow) != OperationStatus.Done)
 785                {
 786                    break;
 787                }
 788
 789                totalCharCount += value.Utf16SequenceLength;
 790                if (totalCharCount < 0)
 791                {
 792                    ThrowConversionOverflow();
 793                }
 794
 795                bytes = bytes.Slice(bytesConsumedJustNow);
 796            }
 797
 798            bytesConsumed = bytesLength - bytes.Length; // number of bytes consumed across all loop iterations above
 799            return totalCharCount;
 800#else
 0801            Debug.Fail("This should be overridden by a subclassed type.");
 802            throw NotImplemented.ByDesign;
 803#endif
 804        }
 805
 806        /// <summary>
 807        /// Counts the number of chars that would result from transcoding the provided bytes,
 808        /// with no associated <see cref="DecoderNLS"/>. The first two arguments are based on the
 809        /// original input before invoking this method; and <paramref name="bytesConsumedSoFar"/>
 810        /// signals where in the provided buffer the fallback loop should begin operating.
 811        /// </summary>
 812        /// <returns>
 813        /// The char count resulting from transcoding the input data.
 814        /// </returns>
 815        /// <exception cref="ArgumentException">
 816        /// If the resulting char count is greater than <see cref="int.MaxValue"/>.
 817        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 818        /// </exception>
 819        [MethodImpl(MethodImplOptions.NoInlining)] // don't stack spill spans into our caller
 820        private protected unsafe int GetCharCountWithFallback(byte* pBytesOriginal, int originalByteCount, int bytesCons
 821        {
 822            // This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
 823            // into our immediate caller. Doing so increases the method prolog in what's supposed to
 824            // be a very fast path.
 825
 9638826            Debug.Assert(0 <= bytesConsumedSoFar && bytesConsumedSoFar < originalByteCount, "Invalid arguments provided 
 827
 9638828            return GetCharCountWithFallback(
 9638829                bytes: new ReadOnlySpan<byte>(pBytesOriginal, originalByteCount).Slice(bytesConsumedSoFar),
 9638830                originalBytesLength: originalByteCount,
 9638831                decoder: null);
 832        }
 833
 834        /// <summary>
 835        /// Gets the number of <see langword="char"/>s that would result from transcoding the provided
 836        /// input data, with an associated <see cref="DecoderNLS"/>. The first two arguments are
 837        /// based on the original input before invoking this method; and <paramref name="bytesConsumedSoFar"/>
 838        /// signals where in the provided source buffer the fallback loop should begin operating.
 839        /// The behavior of this method is to consume (non-destructively) any leftover data in the
 840        /// <see cref="DecoderNLS"/> instance, then to invoke the <see cref="GetCharCountFast"/> virtual method
 841        /// after data has been drained, then to call <see cref="GetCharCountWithFallback(ReadOnlySpan{byte}, int, Decod
 842        /// </summary>
 843        /// <returns>
 844        /// The total number of chars that would result from transcoding the remaining portion of the source buffer.
 845        /// </returns>
 846        /// <exception cref="ArgumentException">
 847        /// If the return value would exceed <see cref="int.MaxValue"/>.
 848        /// (The implementation should call <see cref="ThrowConversionOverflow"/>.)
 849        /// </exception>
 850        private unsafe int GetCharCountWithFallback(byte* pOriginalBytes, int originalByteCount, int bytesConsumedSoFar,
 851        {
 0852            Debug.Assert(decoder != null, "This code path should only be called from DecoderNLS.");
 0853            Debug.Assert(0 <= bytesConsumedSoFar && bytesConsumedSoFar <= originalByteCount, "Caller should've checked t
 854
 855            // First, try draining any data that already exists on the decoder instance. If we can't complete
 856            // that operation, there's no point to continuing down to the main workhorse methods.
 857
 0858            ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(pOriginalBytes, originalByteCount).Slice(bytesConsumedSoFa
 859
 860            int bytesConsumedJustNow;
 0861            int totalCharCount = 0;
 862
 0863            if (decoder.HasLeftoverData)
 864            {
 0865                totalCharCount = decoder.DrainLeftoverDataForGetCharCount(bytes, out bytesConsumedJustNow);
 0866                bytes = bytes.Slice(bytesConsumedJustNow);
 867            }
 868
 869            // Now try invoking the "fast path" (no fallback) implementation.
 870            // We can use Unsafe.AsPointer here since these spans are created from pinned data (raw pointers).
 871
 0872            totalCharCount += GetCharCountFast(
 0873                pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 0874                bytesLength: bytes.Length,
 0875                fallback: decoder.Fallback,
 0876                bytesConsumed: out bytesConsumedJustNow);
 877
 0878            if (totalCharCount < 0)
 879            {
 0880                ThrowConversionOverflow();
 881            }
 882
 0883            bytes = bytes.Slice(bytesConsumedJustNow);
 884
 885            // If there's still data remaining in the source buffer, go down the fallback path.
 886            // Otherwise we're finished.
 887
 0888            if (!bytes.IsEmpty)
 889            {
 0890                totalCharCount += GetCharCountWithFallback(bytes, originalByteCount, decoder);
 0891                if (totalCharCount < 0)
 892                {
 0893                    ThrowConversionOverflow();
 894                }
 895            }
 896
 0897            return totalCharCount;
 898        }
 899
 900        /// <summary>
 901        /// Counts the number of chars that would result from transcoding the provided bytes,
 902        /// using the provided <see cref="DecoderFallbackBuffer"/> if necessary.
 903        /// </summary>
 904        /// <returns>
 905        /// The char count resulting from transcoding the input data.
 906        /// </returns>
 907        /// <exception cref="ArgumentException">
 908        /// If the resulting char count is greater than <see cref="int.MaxValue"/>.
 909        /// (Implementation should call <see cref="ThrowConversionOverflow"/>.)
 910        /// </exception>
 911        private unsafe int GetCharCountWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, DecoderNLS? decod
 912        {
 9638913            Debug.Assert(!bytes.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
 9638914            Debug.Assert(originalBytesLength >= 0, "Caller provided invalid parameter.");
 915
 916            // Since we're using Unsafe.AsPointer in our central loop, we want to ensure everything is pinned.
 917
 9638918            fixed (byte* _pBytes_Unused = &MemoryMarshal.GetReference(bytes))
 919            {
 9638920                DecoderFallbackBuffer fallbackBuffer = DecoderFallbackBuffer.CreateAndInitialize(this, decoder, original
 9638921                int totalCharCount = 0;
 922
 923                do
 924                {
 925                    // There's still data in the source buffer; why wasn't the previous fast-path able to consume it ful
 926                    // There are two scenarios: (a) the source buffer contained invalid data, or it contained incomplete
 927
 667100928                    if (DecodeFirstRune(bytes, out Rune firstScalarValue, out int bytesConsumedThisIteration) == Operati
 667100929                          && decoder != null
 667100930                          && !decoder.MustFlush)
 931                    {
 932                        // We saw incomplete data at the end of the buffer, and the active DecoderNLS isntance
 933                        // isn't asking us to flush. Since a call to GetChars would've consumed this data by
 934                        // storing it in the DecoderNLS instance, we'll "consume" it by ignoring it.
 935                        // The next call to GetChars will pick it up correctly.
 936
 937                        goto Finish;
 938                    }
 939
 940                    // We saw invalid binary data, or we saw incomplete data that we need to flush (and thus
 941                    // treat as invalid). In any case we'll run through the fallback mechanism.
 942
 667100943                    int charCountThisIteration = fallbackBuffer.InternalFallbackGetCharCount(bytes, bytesConsumedThisIte
 944
 667100945                    Debug.Assert(charCountThisIteration >= 0, "Fallback shouldn't have returned a negative value.");
 946
 667100947                    totalCharCount += charCountThisIteration;
 667100948                    if (totalCharCount < 0)
 949                    {
 0950                        ThrowConversionOverflow();
 951                    }
 952
 667100953                    bytes = bytes.Slice(bytesConsumedThisIteration);
 954
 667100955                    if (!bytes.IsEmpty)
 956                    {
 957                        // Still data remaining - run it through the fast-path to find the next data to fallback.
 958                        // While building up the tally we need to continually check for integer overflow
 959                        // since fallbacks can change the total byte count in unexpected ways.
 960
 661486961                        charCountThisIteration = GetCharCountFast(
 661486962                            pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 661486963                            bytesLength: bytes.Length,
 661486964                            fallback: null, // wasn't able to be short-circuited by our caller; don't bother trying agai
 661486965                            bytesConsumed: out bytesConsumedThisIteration);
 966
 661486967                        Debug.Assert(charCountThisIteration >= 0, "Workhorse shouldn't have returned a negative value.")
 661486968                        Debug.Assert(bytesConsumedThisIteration >= 0, "Workhorse shouldn't have returned a negative valu
 969
 661486970                        totalCharCount += charCountThisIteration;
 661486971                        if (totalCharCount < 0)
 972                        {
 0973                            ThrowConversionOverflow();
 974                        }
 975
 661486976                        bytes = bytes.Slice(bytesConsumedThisIteration);
 977                    }
 667100978                } while (!bytes.IsEmpty);
 979
 980            Finish:
 981
 9638982                Debug.Assert(fallbackBuffer.Remaining == 0, "There should be no data in the fallback buffer after GetCha
 983
 9638984                return totalCharCount;
 985            }
 986        }
 987
 988        /*
 989         * GETCHARS FAMILY OF FUNCTIONS
 990         */
 991
 992        /// <summary>
 993        /// Entry point from <see cref="DecoderNLS.GetChars"/> and <see cref="DecoderNLS.Convert"/>.
 994        /// </summary>
 995        internal virtual unsafe int GetChars(byte* pBytes, int byteCount, char* pChars, int charCount, DecoderNLS? decod
 996        {
 0997            Debug.Assert(decoder != null, "This code path should only be called from DecoderNLS.");
 0998            Debug.Assert(byteCount >= 0, "Caller should've checked this condition.");
 0999            Debug.Assert(pBytes != null || byteCount == 0, "Cannot provide a null pointer and a non-zero count.");
 01000            Debug.Assert(charCount >= 0, "Caller should've checked this condition.");
 01001            Debug.Assert(pChars != null || charCount == 0, "Cannot provide a null pointer and a non-zero count.");
 1002
 1003            // We're going to try to stay on the fast-path as much as we can. That means that we have
 1004            // no leftover data to drain and the entire source buffer can be transcoded in a single
 1005            // fast-path invocation. If either of these doesn't hold, we'll go down the slow path of
 1006            // creating spans, draining the DecoderNLS instance, and falling back.
 1007
 01008            int charsWritten = 0;
 01009            int bytesConsumed = 0;
 1010
 01011            if (!decoder.HasLeftoverData)
 1012            {
 01013                charsWritten = GetCharsFast(pBytes, byteCount, pChars, charCount, out bytesConsumed);
 01014                if (bytesConsumed == byteCount)
 1015                {
 01016                    decoder._bytesUsed = byteCount;
 01017                    return charsWritten;
 1018                }
 1019            }
 1020
 1021            // We had leftover data, or we couldn't consume the entire input buffer.
 1022            // Let's go down the draining + fallback mechanisms.
 1023
 01024            return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten, decoder);
 1025        }
 1026
 1027        /// <summary>
 1028        /// Transcodes <see langword="byte"/>s to <see langword="char"/>s, exiting when the source or destination
 1029        /// buffer is consumed or when the first unreadable data is encountered.
 1030        /// </summary>
 1031        /// <returns>
 1032        /// Via <paramref name="bytesConsumed"/>, the number of elements from <paramref name="pBytes"/> which
 1033        /// were consumed; and returns the number of elements written to <paramref name="pChars"/>.
 1034        /// </returns>
 1035        /// <remarks>
 1036        /// The implementation should not attempt to perform any sort of fallback behavior.
 1037        /// If custom fallback behavior is necessary, override <see cref="GetCharsWithFallback"/>.
 1038        /// </remarks>
 1039        private protected virtual unsafe int GetCharsFast(byte* pBytes, int bytesLength, char* pChars, int charsLength, 
 1040        {
 1041            // Any production-quality type would override this method and provide a real
 1042            // implementation, so we won't provide a base implementation. However, a
 1043            // non-shipping slow reference implementation is provided below for convenience.
 1044
 1045#if false
 1046            ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(pBytes, bytesLength);
 1047            Span<char> chars = new Span<char>(pChars, charsLength);
 1048
 1049            while (!bytes.IsEmpty)
 1050            {
 1051                if ((DecodeFirstRune(bytes, out Rune firstScalarValue, out int bytesConsumedJustNow) != OperationStatus.
 1052                    || !firstScalarValue.TryEncode(chars, out int charsWrittenJustNow))
 1053                {
 1054                    // Invalid or incomplete binary data, or destination buffer too small to contain decoded value
 1055
 1056                    break;
 1057                }
 1058
 1059                bytes = bytes.Slice(bytesConsumedJustNow);
 1060                chars = chars.Slice(charsWrittenJustNow);
 1061            }
 1062
 1063            bytesConsumed = bytesLength - bytes.Length; // number of bytes consumed across all loop iterations above
 1064            return charsLength - chars.Length; // number of chars written across all loop iterations above
 1065#else
 01066            Debug.Fail("This should be overridden by a subclassed type.");
 1067            throw NotImplemented.ByDesign;
 1068#endif
 1069        }
 1070
 1071        /// <summary>
 1072        /// Transcodes bytes to chars, with no associated <see cref="DecoderNLS"/>. The first four arguments are
 1073        /// based on the original input before invoking this method; and <paramref name="bytesConsumedSoFar"/>
 1074        /// and <paramref name="charsWrittenSoFar"/> signal where in the provided buffers the fallback loop
 1075        /// should begin operating. The behavior of this method is to call the <see cref="GetCharsWithFallback"/>
 1076        /// virtual method as overridden by the specific type, and failing that go down the shared fallback path.
 1077        /// </summary>
 1078        /// <returns>
 1079        /// The total number of chars written to <paramref name="pOriginalChars"/>, including <paramref name="charsWritt
 1080        /// </returns>
 1081        /// <exception cref="ArgumentException">
 1082        /// If the destination buffer is not large enough to hold the entirety of the transcoded data.
 1083        /// </exception>
 1084        [MethodImpl(MethodImplOptions.NoInlining)]
 1085        private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalCh
 1086        {
 1087            // This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
 1088            // into our immediate caller. Doing so increases the method prolog in what's supposed to
 1089            // be a very fast path.
 1090
 96381091            Debug.Assert(0 <= bytesConsumedSoFar && bytesConsumedSoFar < originalByteCount, "Invalid arguments provided 
 96381092            Debug.Assert(0 <= charsWrittenSoFar && charsWrittenSoFar <= originalCharCount, "Invalid arguments provided t
 1093
 96381094            return GetCharsWithFallback(
 96381095                bytes: new ReadOnlySpan<byte>(pOriginalBytes, originalByteCount).Slice(bytesConsumedSoFar),
 96381096                originalBytesLength: originalByteCount,
 96381097                chars: new Span<char>(pOriginalChars, originalCharCount).Slice(charsWrittenSoFar),
 96381098                originalCharsLength: originalCharCount,
 96381099                decoder: null,
 96381100                throwForDestinationOverflow);
 1101        }
 1102
 1103        /// <summary>
 1104        /// Transcodes bytes to chars, with an associated <see cref="DecoderNLS"/>. The first four arguments are
 1105        /// based on the original input before invoking this method; and <paramref name="bytesConsumedSoFar"/>
 1106        /// and <paramref name="charsWrittenSoFar"/> signal where in the provided buffers the fallback loop
 1107        /// should begin operating. The behavior of this method is to drain any leftover data in the
 1108        /// <see cref="DecoderNLS"/> instance, then to invoke the <see cref="GetCharsFast"/> virtual method
 1109        /// after data has been drained, then to call GetCharsWithFallback(ReadOnlySpan{byte}, int, Span{char}, int, Dec
 1110        /// </summary>
 1111        /// <returns>
 1112        /// The total number of chars written to <paramref name="pOriginalChars"/>, including <paramref name="charsWritt
 1113        /// </returns>
 1114        /// <exception cref="ArgumentException">
 1115        /// If the destination buffer is too small to make any forward progress at all, or if the destination buffer is
 1116        /// too small to contain the entirety of the transcoded data and the <see cref="DecoderNLS"/> instance disallows
 1117        /// partial transcoding.
 1118        /// </exception>
 1119        private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalCh
 1120        {
 01121            Debug.Assert(decoder != null, "This code path should only be called from DecoderNLS.");
 01122            Debug.Assert(0 <= bytesConsumedSoFar && bytesConsumedSoFar <= originalByteCount, "Caller should've checked t
 01123            Debug.Assert(0 <= charsWrittenSoFar && charsWrittenSoFar <= originalCharCount, "Caller should've checked thi
 1124
 1125            // First, try draining any data that already exists on the encoder instance. If we can't complete
 1126            // that operation, there's no point to continuing down to the main workhorse methods.
 1127            //
 1128            // Like GetBytes, there may be leftover data in the DecoderNLS instance. But unlike GetBytes,
 1129            // the bytes -> chars conversion doesn't allow leftover data in the fallback buffer. This means
 1130            // that the drain operation below will either succeed fully or fail; there's no partial success
 1131            // condition as with the chars -> bytes conversion. The drain method will throw if there's not
 1132            // enough space in the destination buffer.
 1133
 01134            ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(pOriginalBytes, originalByteCount).Slice(bytesConsumedSoFa
 01135            Span<char> chars = new Span<char>(pOriginalChars, originalCharCount).Slice(charsWrittenSoFar);
 1136
 1137            int bytesConsumedJustNow;
 1138            int charsWrittenJustNow;
 1139
 01140            if (decoder.HasLeftoverData)
 1141            {
 01142                charsWrittenJustNow = decoder.DrainLeftoverDataForGetChars(bytes, chars, out bytesConsumedJustNow);
 01143                bytes = bytes.Slice(bytesConsumedJustNow);
 01144                chars = chars.Slice(charsWrittenJustNow);
 1145            }
 1146
 01147            Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "Should be no rema
 1148
 1149            // Now try invoking the "fast path" (no fallback buffer) implementation.
 1150            // We can use Unsafe.AsPointer here since these spans are created from pinned data (raw pointers).
 1151
 01152            charsWrittenJustNow = GetCharsFast(
 01153                pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 01154                bytesLength: bytes.Length,
 01155                pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 01156                charsLength: chars.Length,
 01157                bytesConsumed: out bytesConsumedJustNow);
 1158
 01159            bytes = bytes.Slice(bytesConsumedJustNow);
 01160            chars = chars.Slice(charsWrittenJustNow);
 1161
 1162            // We'll optimistically tell the decoder that we're using everything; the
 1163            // GetCharsWithFallback method will overwrite this field if necessary.
 1164
 01165            decoder._bytesUsed = originalByteCount;
 1166
 01167            if (bytes.IsEmpty)
 1168            {
 01169                return originalCharCount - chars.Length; // total number of chars written
 1170            }
 1171            else
 1172            {
 01173                return GetCharsWithFallback(bytes, originalByteCount, chars, originalCharCount, decoder);
 1174            }
 1175        }
 1176
 1177        /// <summary>
 1178        /// Transcodes bytes to chars, using <see cref="DecoderFallback"/> or <see cref="Decoder.Fallback"/> if needed.
 1179        /// </summary>
 1180        /// <returns>
 1181        /// The total number of chars written to <paramref name="chars"/> (based on <paramref name="originalCharsLength"
 1182        /// </returns>
 1183        /// <remarks>
 1184        /// The derived class should override this method if it might be able to provide a more optimized fallback
 1185        /// implementation, deferring to the base implementation if needed. This method calls <see cref="ThrowCharsOverf
 1186        /// if necessary.
 1187        /// </remarks>
 1188        private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Spa
 1189        {
 48191190            Debug.Assert(!bytes.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
 48191191            Debug.Assert(originalBytesLength >= 0, "Caller provided invalid parameter.");
 48191192            Debug.Assert(originalCharsLength >= 0, "Caller provided invalid parameter.");
 1193
 1194            // Since we're using Unsafe.AsPointer in our central loop, we want to ensure everything is pinned.
 1195
 48191196            fixed (byte* _pBytes_Unused = &MemoryMarshal.GetReference(bytes))
 48191197            fixed (char* _pChars_Unused = &MemoryMarshal.GetReference(chars))
 1198            {
 48191199                DecoderFallbackBuffer fallbackBuffer = DecoderFallbackBuffer.CreateAndInitialize(this, decoder, original
 1200
 1201                do
 1202                {
 1203                    // There's still data in the source buffer; why wasn't the previous fast-path able to consume it ful
 1204                    // There are two scenarios: (a) the source buffer contained invalid data, or it contained incomplete
 1205
 1206                    int charsWrittenThisIteration;
 1207
 3335501208                    switch (DecodeFirstRune(bytes, out _, out int bytesConsumedThisIteration))
 1209                    {
 1210                        case OperationStatus.NeedMoreData:
 9881211                            Debug.Assert(bytesConsumedThisIteration == bytes.Length, "If returning NeedMoreData, should 
 9881212                            if (decoder is null || decoder.MustFlush)
 1213                            {
 1214                                goto case OperationStatus.InvalidData; // see comment in GetCharCountWithFallback
 1215                            }
 1216                            else
 1217                            {
 01218                                decoder.SetLeftoverData(bytes); // squirrel away remaining data and finish
 01219                                bytes = ReadOnlySpan<byte>.Empty;
 01220                                goto Finish;
 1221                            }
 1222
 1223                        case OperationStatus.InvalidData:
 3335501224                            if (fallbackBuffer.TryInternalFallbackGetChars(bytes, bytesConsumedThisIteration, chars, out
 1225                            {
 1226                                // We successfully consumed some bytes, sent it through the fallback, and wrote some cha
 1227
 3335501228                                Debug.Assert(charsWrittenThisIteration >= 0, "Fallback shouldn't have returned a negativ
 1229                                break;
 1230                            }
 1231                            else
 1232                            {
 1233                                // We generated fallback data, but the destination buffer wasn't large enough to hold it
 1234                                // Don't mark any of the bytes we ran through the fallback as consumed, and terminate
 1235                                // the loop now and let our caller handle this condition.
 1236
 1237                                goto Finish;
 1238                            }
 1239
 1240                        default:
 1241                            goto Finish; // no error on input, so destination must have been too small
 1242                    }
 1243
 3335501244                    bytes = bytes.Slice(bytesConsumedThisIteration);
 3335501245                    chars = chars.Slice(charsWrittenThisIteration);
 1246
 3335501247                    if (!bytes.IsEmpty)
 1248                    {
 1249                        // Still data remaining - run it through the fast-path to find the next data to fallback.
 1250                        // We need to figure out why we weren't able to make progress.
 1251
 3307431252                        charsWrittenThisIteration = GetCharsFast(
 3307431253                            pBytes: (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(bytes)),
 3307431254                            bytesLength: bytes.Length,
 3307431255                            pChars: (char*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(chars)),
 3307431256                            charsLength: chars.Length,
 3307431257                            bytesConsumed: out bytesConsumedThisIteration);
 1258
 3307431259                        Debug.Assert(charsWrittenThisIteration >= 0, "Workhorse shouldn't have returned a negative value
 3307431260                        Debug.Assert(bytesConsumedThisIteration >= 0, "Workhorse shouldn't have returned a negative valu
 1261
 3307431262                        bytes = bytes.Slice(bytesConsumedThisIteration);
 3307431263                        chars = chars.Slice(charsWrittenThisIteration);
 1264                    }
 3335501265                } while (!bytes.IsEmpty);
 1266
 1267            Finish:
 1268
 1269                // We reach this point when we deplete the source or destination buffer. See main comment
 1270                // at the end of GetBytesWithFallback for how the below logic works; the primary difference
 1271                // here is that GetChars disallows leftover data in the fallback buffer between calls.
 1272
 48191273                Debug.Assert(fallbackBuffer.Remaining == 0);
 1274
 48191275                if (!bytes.IsEmpty)
 1276                {
 01277                    if (throwForDestinationOverflow)
 1278                    {
 1279                        // The line below will also throw if the decoder couldn't make any progress at all
 1280                        // because the output buffer wasn't large enough to contain the result of even
 1281                        // a single scalar conversion or fallback.
 01282                        ThrowCharsOverflow(decoder, nothingDecoded: chars.Length == originalCharsLength);
 1283                    }
 1284                    else
 1285                    {
 01286                        Debug.Assert(decoder is null);
 01287                        return -1;
 1288                    }
 1289                }
 1290
 1291                // If a DecoderNLS instance is active, update its "total consumed byte count" value.
 1292
 48191293                if (decoder != null)
 1294                {
 01295                    Debug.Assert(originalBytesLength >= bytes.Length, "About to report a negative number of bytes used?"
 01296                    decoder._bytesUsed = originalBytesLength - bytes.Length; // number of bytes consumed
 1297                }
 1298
 48191299                return originalCharsLength - chars.Length; // total number of chars written
 1300            }
 1301        }
 1302    }
 1303}
 1304

Methods/Properties

.cctor()
Default()
.ctor(System.Int32)
.ctor()
.ctor(System.Int32,System.Text.EncoderFallback,System.Text.DecoderFallback)
SetDefaultFallbacks()
Convert(System.Text.Encoding,System.Text.Encoding,System.Byte[])
Convert(System.Text.Encoding,System.Text.Encoding,System.Byte[],System.Int32,System.Int32)
RegisterProvider(System.Text.EncodingProvider)
GetEncoding(System.Int32)
GetEncoding(System.Int32,System.Text.EncoderFallback,System.Text.DecoderFallback)
GetEncoding(System.String)
GetEncoding(System.String,System.Text.EncoderFallback,System.Text.DecoderFallback)
FilterDisallowedEncodings(System.Text.Encoding)
GetEncodings()
GetPreamble()
Preamble()
GetDataItem()
BodyName()
EncodingName()
HeaderName()
WebName()
WindowsCodePage()
IsBrowserDisplay()
IsBrowserSave()
IsMailNewsDisplay()
IsMailNewsSave()
IsSingleByte()
EncoderFallback()
EncoderFallback(System.Text.EncoderFallback)
DecoderFallback()
DecoderFallback(System.Text.DecoderFallback)
Clone()
IsReadOnly()
IsReadOnly(System.Boolean)
ASCII()
Latin1()
GetByteCount(System.Char[])
GetByteCount(System.String)
GetByteCount(System.String,System.Int32,System.Int32)
GetByteCount(System.Char*,System.Int32)
GetByteCount(System.ReadOnlySpan`1<System.Char>)
GetBytes(System.Char[])
GetBytes(System.Char[],System.Int32,System.Int32)
GetBytes(System.String)
GetBytes(System.String,System.Int32,System.Int32)
GetBytes(System.String,System.Int32,System.Int32,System.Byte[],System.Int32)
GetBytes(System.Char*,System.Int32,System.Byte*,System.Int32)
GetBytes(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Byte>)
TryGetBytes(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Byte>,System.Int32&)
GetCharCount(System.Byte[])
GetCharCount(System.Byte*,System.Int32)
GetCharCount(System.ReadOnlySpan`1<System.Byte>)
GetChars(System.Byte[])
GetChars(System.Byte[],System.Int32,System.Int32)
GetChars(System.Byte*,System.Int32,System.Char*,System.Int32)
GetChars(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Char>)
TryGetChars(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Char>,System.Int32&)
GetString(System.Byte*,System.Int32)
GetString(System.ReadOnlySpan`1<System.Byte>)
CodePage()
IsUTF8CodePage()
IsAlwaysNormalized()
IsAlwaysNormalized(System.Text.NormalizationForm)
GetDecoder()
GetEncoder()
GetString(System.Byte[])
GetString(System.Byte[],System.Int32,System.Int32)
Unicode()
BigEndianUnicode()
UTF7()
UTF8()
UTF32()
BigEndianUTF32()
Equals(System.Object)
GetHashCode()
CreateTranscodingStream(System.IO.Stream,System.Text.Encoding,System.Text.Encoding,System.Boolean)
ThrowBytesOverflow()
ThrowBytesOverflow(System.Text.EncoderNLS,System.Boolean)
ThrowConversionOverflow()
ThrowCharsOverflow()
ThrowCharsOverflow(System.Text.DecoderNLS,System.Boolean)
.ctor(System.Text.Encoding)
GetByteCount(System.Char[],System.Int32,System.Int32,System.Boolean)
GetByteCount(System.Char*,System.Int32,System.Boolean)
GetBytes(System.Char[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Boolean)
GetBytes(System.Char*,System.Int32,System.Byte*,System.Int32,System.Boolean)
.ctor(System.Text.Encoding)
GetCharCount(System.Byte[],System.Int32,System.Int32)
GetCharCount(System.Byte[],System.Int32,System.Int32,System.Boolean)
GetCharCount(System.Byte*,System.Int32,System.Boolean)
GetChars(System.Byte[],System.Int32,System.Int32,System.Char[],System.Int32)
GetChars(System.Byte[],System.Int32,System.Int32,System.Char[],System.Int32,System.Boolean)
GetChars(System.Byte*,System.Int32,System.Char*,System.Int32,System.Boolean)
.ctor(System.Text.Encoding,System.Text.DecoderNLS,System.Char*,System.Int32,System.Byte*,System.Int32)
AddChar(System.Char,System.Int32)
AddChar(System.Char)
AdjustBytes(System.Int32)
MoreData()
GetNextByte()
BytesUsed()
Fallback(System.Byte)
Fallback(System.Byte[])
Count()
.ctor(System.Text.Encoding,System.Text.EncoderNLS,System.Byte*,System.Int32,System.Char*,System.Int32)
AddByte(System.Byte,System.Int32)
AddByte(System.Byte)
AddByte(System.Byte,System.Byte)
AddByte(System.Byte,System.Byte,System.Int32)
MovePrevious(System.Boolean)
MoreData()
GetNextChar()
CharsUsed()
Count()
DecodeFirstRune(System.ReadOnlySpan`1<System.Byte>,System.Text.Rune&,System.Int32&)
EncodeRune(System.Text.Rune,System.Span`1<System.Byte>,System.Int32&)
TryGetByteCount(System.Text.Rune,System.Int32&)
GetByteCount(System.Char*,System.Int32,System.Text.EncoderNLS)
GetByteCountFast(System.Char*,System.Int32,System.Text.EncoderFallback,System.Int32&)
GetByteCountWithFallback(System.Char*,System.Int32,System.Int32)
GetByteCountWithFallback(System.Char*,System.Int32,System.Int32,System.Text.EncoderNLS)
GetByteCountWithFallback(System.ReadOnlySpan`1<System.Char>,System.Int32,System.Text.EncoderNLS)
GetBytes(System.Char*,System.Int32,System.Byte*,System.Int32,System.Text.EncoderNLS)
GetBytesFast(System.Char*,System.Int32,System.Byte*,System.Int32,System.Int32&)
GetBytesWithFallback(System.Char*,System.Int32,System.Byte*,System.Int32,System.Int32,System.Int32,System.Boolean)
GetBytesWithFallback(System.Char*,System.Int32,System.Byte*,System.Int32,System.Int32,System.Int32,System.Text.EncoderNLS)
GetBytesWithFallback(System.ReadOnlySpan`1<System.Char>,System.Int32,System.Span`1<System.Byte>,System.Int32,System.Text.EncoderNLS,System.Boolean)
GetCharCount(System.Byte*,System.Int32,System.Text.DecoderNLS)
GetCharCountFast(System.Byte*,System.Int32,System.Text.DecoderFallback,System.Int32&)
GetCharCountWithFallback(System.Byte*,System.Int32,System.Int32)
GetCharCountWithFallback(System.Byte*,System.Int32,System.Int32,System.Text.DecoderNLS)
GetCharCountWithFallback(System.ReadOnlySpan`1<System.Byte>,System.Int32,System.Text.DecoderNLS)
GetChars(System.Byte*,System.Int32,System.Char*,System.Int32,System.Text.DecoderNLS)
GetCharsFast(System.Byte*,System.Int32,System.Char*,System.Int32,System.Int32&)
GetCharsWithFallback(System.Byte*,System.Int32,System.Char*,System.Int32,System.Int32,System.Int32,System.Boolean)
GetCharsWithFallback(System.Byte*,System.Int32,System.Char*,System.Int32,System.Int32,System.Int32,System.Text.DecoderNLS)
GetCharsWithFallback(System.ReadOnlySpan`1<System.Byte>,System.Int32,System.Span`1<System.Char>,System.Int32,System.Text.DecoderNLS,System.Boolean)