| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Collections.Generic; |
| | | 5 | | using System.Diagnostics; |
| | | 6 | | using System.Diagnostics.CodeAnalysis; |
| | | 7 | | using System.Globalization; |
| | | 8 | | using System.IO; |
| | | 9 | | using System.Runtime.InteropServices; |
| | | 10 | | using System.Runtime.Serialization; |
| | | 11 | | |
| | | 12 | | namespace 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 |
| | 1 | 80 | | private static readonly UTF8Encoding.UTF8EncodingSealed s_defaultEncoding = new UTF8Encoding.UTF8EncodingSealed( |
| | | 81 | | |
| | | 82 | | // Returns an encoding for the system's current ANSI code page. |
| | 1 | 83 | | 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)] |
| | 6 | 119 | | private bool _isReadOnly = true; |
| | | 120 | | |
| | | 121 | | // Encoding (encoder) fallback |
| | | 122 | | internal EncoderFallback encoderFallback; |
| | | 123 | | internal DecoderFallback decoderFallback; |
| | | 124 | | |
| | 1 | 125 | | protected Encoding() : this(0) |
| | | 126 | | { |
| | 1 | 127 | | } |
| | | 128 | | |
| | 6 | 129 | | protected Encoding(int codePage) |
| | | 130 | | { |
| | | 131 | | // Validate code page |
| | 6 | 132 | | ArgumentOutOfRangeException.ThrowIfNegative(codePage); |
| | | 133 | | |
| | | 134 | | // Remember code page |
| | 6 | 135 | | _codePage = codePage; |
| | | 136 | | |
| | | 137 | | // Use default encoder/decoder fallbacks |
| | 6 | 138 | | this.SetDefaultFallbacks(); |
| | 6 | 139 | | } |
| | | 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. |
| | 0 | 144 | | protected Encoding(int codePage, EncoderFallback? encoderFallback, DecoderFallback? decoderFallback) |
| | | 145 | | { |
| | | 146 | | // Validate code page |
| | 0 | 147 | | ArgumentOutOfRangeException.ThrowIfNegative(codePage); |
| | | 148 | | |
| | | 149 | | // Remember code page |
| | 0 | 150 | | _codePage = codePage; |
| | | 151 | | |
| | 0 | 152 | | this.encoderFallback = encoderFallback ?? EncoderFallback.ReplacementFallback; |
| | 0 | 153 | | this.decoderFallback = decoderFallback ?? DecoderFallback.ReplacementFallback; |
| | 0 | 154 | | } |
| | | 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. |
| | 1 | 163 | | encoderFallback = EncoderFallback.ReplacementFallback; |
| | 1 | 164 | | decoderFallback = DecoderFallback.ReplacementFallback; |
| | 1 | 165 | | } |
| | | 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 | | { |
| | 0 | 174 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 175 | | |
| | 0 | 176 | | 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 | | { |
| | 0 | 187 | | ArgumentNullException.ThrowIfNull(srcEncoding); |
| | 0 | 188 | | ArgumentNullException.ThrowIfNull(dstEncoding); |
| | 0 | 189 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 190 | | |
| | 0 | 191 | | return dstEncoding.GetBytes(srcEncoding.GetChars(bytes, index, count)); |
| | | 192 | | } |
| | | 193 | | |
| | | 194 | | public static void RegisterProvider(EncodingProvider provider) |
| | | 195 | | { |
| | | 196 | | // Parameters validated inside EncodingProvider |
| | 0 | 197 | | EncodingProvider.AddProvider(provider); |
| | 0 | 198 | | } |
| | | 199 | | |
| | | 200 | | public static Encoding GetEncoding(int codepage) |
| | | 201 | | { |
| | 3 | 202 | | Encoding? result = FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(codepage)); |
| | 3 | 203 | | if (result is not null) |
| | 0 | 204 | | return result; |
| | | 205 | | |
| | | 206 | | switch (codepage) |
| | | 207 | | { |
| | 1 | 208 | | case CodePageDefault: return Default; // 0 |
| | 0 | 209 | | case CodePageUnicode: return Unicode; // 1200 |
| | 0 | 210 | | case CodePageBigEndian: return BigEndianUnicode; // 1201 |
| | 0 | 211 | | case CodePageUTF32: return UTF32; // 12000 |
| | 0 | 212 | | case CodePageUTF32BE: return BigEndianUTF32; // 12001 |
| | 2 | 213 | | case CodePageUTF8: return UTF8; // 65001 |
| | 0 | 214 | | case CodePageASCII: return ASCII; // 20127 |
| | 0 | 215 | | 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 |
| | 0 | 222 | | 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 | | |
| | 0 | 231 | | if (LocalAppContextSwitches.EnableUnsafeUTF7Encoding) |
| | | 232 | | { |
| | | 233 | | #pragma warning disable SYSLIB0001 // Encoding.UTF7 property getter is obsolete |
| | 0 | 234 | | return UTF7; |
| | | 235 | | #pragma warning restore SYSLIB0001 |
| | | 236 | | } |
| | | 237 | | else |
| | | 238 | | { |
| | 0 | 239 | | string moreInfoUrl = string.Format(CultureInfo.InvariantCulture, Obsoletions.SharedUrlFormat |
| | 0 | 240 | | string exceptionMessage = SR.Format(SR.Encoding_UTF7_Disabled, moreInfoUrl); |
| | 0 | 241 | | throw new NotSupportedException(exceptionMessage); // matches generic "unknown code page" ex |
| | | 242 | | } |
| | | 243 | | } |
| | | 244 | | } |
| | | 245 | | |
| | 0 | 246 | | if (codepage < 0 || codepage > 65535) |
| | | 247 | | { |
| | 0 | 248 | | throw new ArgumentOutOfRangeException( |
| | 0 | 249 | | nameof(codepage), SR.Format(SR.ArgumentOutOfRange_Range, 0, 65535)); |
| | | 250 | | } |
| | | 251 | | |
| | 0 | 252 | | throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, codepage)); |
| | | 253 | | } |
| | | 254 | | |
| | | 255 | | public static Encoding GetEncoding(int codepage, |
| | | 256 | | EncoderFallback encoderFallback, DecoderFallback decoderFallback) |
| | | 257 | | { |
| | 1 | 258 | | Encoding? baseEncoding = FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(codepage, encode |
| | | 259 | | |
| | 1 | 260 | | if (baseEncoding is not null) |
| | 0 | 261 | | return baseEncoding; |
| | | 262 | | |
| | | 263 | | // Get the default encoding (which is cached and read only) |
| | 1 | 264 | | baseEncoding = GetEncoding(codepage); |
| | | 265 | | |
| | | 266 | | // Clone it and set the fallback |
| | 1 | 267 | | Encoding fallbackEncoding = (Encoding)baseEncoding.Clone(); |
| | 1 | 268 | | fallbackEncoding.EncoderFallback = encoderFallback; |
| | 1 | 269 | | fallbackEncoding.DecoderFallback = decoderFallback; |
| | | 270 | | |
| | 1 | 271 | | 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(). |
| | 0 | 282 | | return FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(name)) ?? |
| | 0 | 283 | | 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(). |
| | 1 | 295 | | return FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(name, encoderFallback, decoderFall |
| | 1 | 296 | | 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 | | { |
| | 5 | 303 | | if (LocalAppContextSwitches.EnableUnsafeUTF7Encoding) |
| | | 304 | | { |
| | 0 | 305 | | return encoding; |
| | | 306 | | } |
| | | 307 | | else |
| | | 308 | | { |
| | 5 | 309 | | 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 | | { |
| | 0 | 319 | | Dictionary<int, EncodingInfo>? result = EncodingProvider.GetEncodingListFromProviders(); |
| | 0 | 320 | | return result is null ? EncodingTable.GetEncodings() : EncodingTable.GetEncodings(result); |
| | | 321 | | } |
| | | 322 | | |
| | 0 | 323 | | public virtual byte[] GetPreamble() => []; |
| | | 324 | | |
| | 1 | 325 | | public virtual ReadOnlySpan<byte> Preamble => GetPreamble(); |
| | | 326 | | |
| | | 327 | | private void GetDataItem() |
| | | 328 | | { |
| | 0 | 329 | | if (_dataItem is null) |
| | | 330 | | { |
| | 0 | 331 | | _dataItem = EncodingTable.GetCodePageDataItem(_codePage); |
| | 0 | 332 | | if (_dataItem is null) |
| | | 333 | | { |
| | 0 | 334 | | throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, _codePage)); |
| | | 335 | | } |
| | | 336 | | } |
| | 0 | 337 | | } |
| | | 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 | | { |
| | 0 | 346 | | if (_dataItem is null) |
| | | 347 | | { |
| | 0 | 348 | | GetDataItem(); |
| | | 349 | | } |
| | 0 | 350 | | 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 | | { |
| | 0 | 359 | | if (_dataItem is null) |
| | | 360 | | { |
| | 0 | 361 | | GetDataItem(); |
| | | 362 | | } |
| | | 363 | | |
| | 0 | 364 | | 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 | | { |
| | 0 | 375 | | if (_dataItem is null) |
| | | 376 | | { |
| | 0 | 377 | | GetDataItem(); |
| | | 378 | | } |
| | 0 | 379 | | return _dataItem!.HeaderName; |
| | | 380 | | } |
| | | 381 | | } |
| | | 382 | | |
| | | 383 | | // Returns the IANA preferred name for this encoding. |
| | | 384 | | public virtual string WebName |
| | | 385 | | { |
| | | 386 | | get |
| | | 387 | | { |
| | 0 | 388 | | if (_dataItem is null) |
| | | 389 | | { |
| | 0 | 390 | | GetDataItem(); |
| | | 391 | | } |
| | 0 | 392 | | 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 | | { |
| | 0 | 402 | | if (_dataItem is null) |
| | | 403 | | { |
| | 0 | 404 | | GetDataItem(); |
| | | 405 | | } |
| | 0 | 406 | | 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 | | { |
| | 0 | 416 | | if (_dataItem is null) |
| | | 417 | | { |
| | 0 | 418 | | GetDataItem(); |
| | | 419 | | } |
| | 0 | 420 | | 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 | | { |
| | 0 | 430 | | if (_dataItem is null) |
| | | 431 | | { |
| | 0 | 432 | | GetDataItem(); |
| | | 433 | | } |
| | 0 | 434 | | 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 | | { |
| | 0 | 444 | | if (_dataItem is null) |
| | | 445 | | { |
| | 0 | 446 | | GetDataItem(); |
| | | 447 | | } |
| | 0 | 448 | | 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 | | { |
| | 0 | 459 | | if (_dataItem is null) |
| | | 460 | | { |
| | 0 | 461 | | GetDataItem(); |
| | | 462 | | } |
| | 0 | 463 | | 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 | | |
| | 0 | 469 | | public virtual bool IsSingleByte => false; |
| | | 470 | | |
| | | 471 | | public EncoderFallback EncoderFallback |
| | | 472 | | { |
| | 1 | 473 | | get => encoderFallback; |
| | | 474 | | set |
| | | 475 | | { |
| | 1 | 476 | | if (this.IsReadOnly) |
| | 0 | 477 | | throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); |
| | | 478 | | |
| | 1 | 479 | | ArgumentNullException.ThrowIfNull(value); |
| | | 480 | | |
| | 1 | 481 | | encoderFallback = value; |
| | 1 | 482 | | } |
| | | 483 | | } |
| | | 484 | | |
| | | 485 | | public DecoderFallback DecoderFallback |
| | | 486 | | { |
| | 24095 | 487 | | get => decoderFallback; |
| | | 488 | | set |
| | | 489 | | { |
| | 1 | 490 | | if (this.IsReadOnly) |
| | 0 | 491 | | throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); |
| | | 492 | | |
| | 1 | 493 | | ArgumentNullException.ThrowIfNull(value); |
| | | 494 | | |
| | 1 | 495 | | decoderFallback = value; |
| | 1 | 496 | | } |
| | | 497 | | } |
| | | 498 | | |
| | | 499 | | public virtual object Clone() |
| | | 500 | | { |
| | 0 | 501 | | Encoding newEncoding = (Encoding)this.MemberwiseClone(); |
| | | 502 | | |
| | | 503 | | // New one should be readable |
| | 0 | 504 | | newEncoding._isReadOnly = false; |
| | 0 | 505 | | return newEncoding; |
| | | 506 | | } |
| | | 507 | | |
| | | 508 | | public bool IsReadOnly |
| | | 509 | | { |
| | 2 | 510 | | get => _isReadOnly; |
| | 1 | 511 | | 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 | | |
| | 0 | 517 | | public static Encoding ASCII => ASCIIEncoding.s_default; |
| | | 518 | | |
| | | 519 | | /// <summary>Gets an encoding for the Latin1 character set (ISO-8859-1).</summary> |
| | 0 | 520 | | 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 | | { |
| | 0 | 527 | | ArgumentNullException.ThrowIfNull(chars); |
| | | 528 | | |
| | 0 | 529 | | return GetByteCount(chars, 0, chars.Length); |
| | | 530 | | } |
| | | 531 | | |
| | | 532 | | public virtual int GetByteCount(string s) |
| | | 533 | | { |
| | 0 | 534 | | if (s is null) |
| | | 535 | | { |
| | 0 | 536 | | ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); |
| | | 537 | | } |
| | | 538 | | |
| | 0 | 539 | | char[] chars = s.ToCharArray(); |
| | 0 | 540 | | 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 | | { |
| | 0 | 552 | | ArgumentNullException.ThrowIfNull(s); |
| | 0 | 553 | | ArgumentOutOfRangeException.ThrowIfNegative(index); |
| | 0 | 554 | | ArgumentOutOfRangeException.ThrowIfNegative(count); |
| | 0 | 555 | | ArgumentOutOfRangeException.ThrowIfGreaterThan(index, s.Length - count); |
| | | 556 | | |
| | | 557 | | unsafe |
| | | 558 | | { |
| | 0 | 559 | | fixed (char* pChar = s) |
| | | 560 | | { |
| | 0 | 561 | | 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 | | { |
| | 0 | 573 | | ArgumentNullException.ThrowIfNull(chars); |
| | 0 | 574 | | ArgumentOutOfRangeException.ThrowIfNegative(count); |
| | | 575 | | |
| | 0 | 576 | | char[] arrChar = new ReadOnlySpan<char>(chars, count).ToArray(); |
| | | 577 | | |
| | 0 | 578 | | return GetByteCount(arrChar, 0, count); |
| | | 579 | | } |
| | | 580 | | |
| | | 581 | | public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars) |
| | 0 | 582 | | { |
| | 0 | 583 | | fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) |
| | | 584 | | { |
| | 0 | 585 | | 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 | | { |
| | 0 | 594 | | ArgumentNullException.ThrowIfNull(chars); |
| | | 595 | | |
| | 0 | 596 | | 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 | | { |
| | 0 | 604 | | byte[] result = new byte[GetByteCount(chars, index, count)]; |
| | 0 | 605 | | GetBytes(chars, index, count, result, 0); |
| | 0 | 606 | | 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 | | { |
| | 0 | 626 | | ArgumentNullException.ThrowIfNull(s); |
| | | 627 | | |
| | 0 | 628 | | int byteCount = GetByteCount(s); |
| | 0 | 629 | | byte[] bytes = new byte[byteCount]; |
| | 0 | 630 | | int bytesReceived = GetBytes(s, 0, s.Length, bytes, 0); |
| | 0 | 631 | | Debug.Assert(byteCount == bytesReceived); |
| | 0 | 632 | | 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 | | { |
| | 0 | 640 | | ArgumentNullException.ThrowIfNull(s); |
| | 0 | 641 | | ArgumentOutOfRangeException.ThrowIfNegative(index); |
| | 0 | 642 | | ArgumentOutOfRangeException.ThrowIfNegative(count); |
| | 0 | 643 | | ArgumentOutOfRangeException.ThrowIfGreaterThan(index, s.Length - count); |
| | | 644 | | |
| | | 645 | | unsafe |
| | | 646 | | { |
| | 0 | 647 | | fixed (char* pChar = s) |
| | | 648 | | { |
| | 0 | 649 | | int byteCount = GetByteCount(pChar + index, count); |
| | 0 | 650 | | if (byteCount == 0) |
| | 0 | 651 | | return []; |
| | | 652 | | |
| | 0 | 653 | | byte[] bytes = new byte[byteCount]; |
| | 0 | 654 | | fixed (byte* pBytes = &bytes[0]) |
| | | 655 | | { |
| | 0 | 656 | | int bytesReceived = GetBytes(pChar + index, count, pBytes, byteCount); |
| | 0 | 657 | | Debug.Assert(byteCount == bytesReceived); |
| | | 658 | | } |
| | 0 | 659 | | return bytes; |
| | | 660 | | } |
| | | 661 | | } |
| | | 662 | | } |
| | | 663 | | |
| | | 664 | | public virtual int GetBytes(string s, int charIndex, int charCount, |
| | | 665 | | byte[] bytes, int byteIndex) |
| | | 666 | | { |
| | 0 | 667 | | if (s is null) |
| | | 668 | | { |
| | 0 | 669 | | ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); |
| | | 670 | | } |
| | | 671 | | |
| | 0 | 672 | | 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 | | { |
| | 0 | 696 | | ArgumentNullException.ThrowIfNull(chars); |
| | 0 | 697 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 698 | | |
| | 0 | 699 | | ArgumentOutOfRangeException.ThrowIfNegative(charCount); |
| | 0 | 700 | | ArgumentOutOfRangeException.ThrowIfNegative(byteCount); |
| | | 701 | | |
| | | 702 | | // Get the char array to convert |
| | 0 | 703 | | char[] arrChar = new ReadOnlySpan<char>(chars, charCount).ToArray(); |
| | | 704 | | |
| | | 705 | | // Get the byte array to fill |
| | 0 | 706 | | byte[] arrByte = new byte[byteCount]; |
| | | 707 | | |
| | | 708 | | // Do the work |
| | 0 | 709 | | int result = GetBytes(arrChar, 0, charCount, arrByte, 0); |
| | | 710 | | |
| | 0 | 711 | | 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 |
| | 0 | 718 | | if (result < byteCount) |
| | 0 | 719 | | byteCount = result; |
| | | 720 | | |
| | | 721 | | // Copy the data, don't overrun our array! |
| | 0 | 722 | | new ReadOnlySpan<byte>(arrByte, 0, byteCount).CopyTo(new Span<byte>(bytes, byteCount)); |
| | | 723 | | |
| | 0 | 724 | | return byteCount; |
| | | 725 | | } |
| | | 726 | | |
| | | 727 | | public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) |
| | 0 | 728 | | { |
| | 0 | 729 | | fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) |
| | 0 | 730 | | fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) |
| | | 731 | | { |
| | 0 | 732 | | 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 | | { |
| | 0 | 743 | | int required = GetByteCount(chars); |
| | 0 | 744 | | if (required <= bytes.Length) |
| | | 745 | | { |
| | 0 | 746 | | bytesWritten = GetBytes(chars, bytes); |
| | 0 | 747 | | return true; |
| | | 748 | | } |
| | | 749 | | |
| | 0 | 750 | | bytesWritten = 0; |
| | 0 | 751 | | 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 | | { |
| | 0 | 759 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 760 | | |
| | 0 | 761 | | 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 | | { |
| | 0 | 774 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 775 | | |
| | 0 | 776 | | ArgumentOutOfRangeException.ThrowIfNegative(count); |
| | | 777 | | |
| | 0 | 778 | | byte[] arrByte = new ReadOnlySpan<byte>(bytes, count).ToArray(); |
| | | 779 | | |
| | 0 | 780 | | return GetCharCount(arrByte, 0, count); |
| | | 781 | | } |
| | | 782 | | |
| | | 783 | | public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes) |
| | 0 | 784 | | { |
| | 0 | 785 | | fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) |
| | | 786 | | { |
| | 0 | 787 | | 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 | | { |
| | 0 | 796 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 797 | | |
| | 0 | 798 | | 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 | | { |
| | 0 | 806 | | char[] result = new char[GetCharCount(bytes, index, count)]; |
| | 0 | 807 | | GetChars(bytes, index, count, result, 0); |
| | 0 | 808 | | 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 | | { |
| | 0 | 845 | | ArgumentNullException.ThrowIfNull(bytes); |
| | 0 | 846 | | ArgumentNullException.ThrowIfNull(chars); |
| | | 847 | | |
| | 0 | 848 | | ArgumentOutOfRangeException.ThrowIfNegative(byteCount); |
| | 0 | 849 | | ArgumentOutOfRangeException.ThrowIfNegative(charCount); |
| | | 850 | | |
| | | 851 | | // Get the byte array to convert |
| | 0 | 852 | | byte[] arrByte = new ReadOnlySpan<byte>(bytes, byteCount).ToArray(); |
| | | 853 | | |
| | | 854 | | // Get the char array to fill |
| | 0 | 855 | | char[] arrChar = new char[charCount]; |
| | | 856 | | |
| | | 857 | | // Do the work |
| | 0 | 858 | | int result = GetChars(arrByte, 0, byteCount, arrChar, 0); |
| | | 859 | | |
| | 0 | 860 | | 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 |
| | 0 | 867 | | if (result < charCount) |
| | 0 | 868 | | charCount = result; |
| | | 869 | | |
| | | 870 | | // Copy the data, don't overrun our array! |
| | 0 | 871 | | new ReadOnlySpan<char>(arrChar, 0, charCount).CopyTo(new Span<char>(chars, charCount)); |
| | | 872 | | |
| | 0 | 873 | | return charCount; |
| | | 874 | | } |
| | | 875 | | |
| | | 876 | | public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars) |
| | 0 | 877 | | { |
| | 0 | 878 | | fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) |
| | 0 | 879 | | fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars)) |
| | | 880 | | { |
| | 0 | 881 | | 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 | | { |
| | 0 | 892 | | int required = GetCharCount(bytes); |
| | 0 | 893 | | if (required <= chars.Length) |
| | | 894 | | { |
| | 0 | 895 | | charsWritten = GetChars(bytes, chars); |
| | 0 | 896 | | return true; |
| | | 897 | | } |
| | | 898 | | |
| | 0 | 899 | | charsWritten = 0; |
| | 0 | 900 | | return false; |
| | | 901 | | } |
| | | 902 | | |
| | | 903 | | [CLSCompliant(false)] |
| | | 904 | | public unsafe string GetString(byte* bytes, int byteCount) |
| | | 905 | | { |
| | 0 | 906 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 907 | | |
| | 0 | 908 | | ArgumentOutOfRangeException.ThrowIfNegative(byteCount); |
| | | 909 | | |
| | 0 | 910 | | return string.CreateStringFromEncoding(bytes, byteCount, this); |
| | | 911 | | } |
| | | 912 | | |
| | | 913 | | public unsafe string GetString(ReadOnlySpan<byte> bytes) |
| | 6497 | 914 | | { |
| | 6497 | 915 | | fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes)) |
| | | 916 | | { |
| | 6497 | 917 | | 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 | | |
| | 4 | 926 | | public virtual int CodePage => _codePage; |
| | | 927 | | |
| | | 928 | | // Quick accessor for "is UTF8?" |
| | 0 | 929 | | 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() => |
| | 0 | 934 | | IsAlwaysNormalized(NormalizationForm.FormC); |
| | | 935 | | |
| | | 936 | | public virtual bool IsAlwaysNormalized(NormalizationForm form) => |
| | | 937 | | // Assume false unless the encoding knows otherwise |
| | 0 | 938 | | 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 | | |
| | 0 | 954 | | 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 | | |
| | 0 | 970 | | 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 | | { |
| | 0 | 1000 | | ArgumentNullException.ThrowIfNull(bytes); |
| | | 1001 | | |
| | 0 | 1002 | | 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) => |
| | 0 | 1011 | | 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 | | |
| | 1 | 1019 | | 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 | | |
| | 0 | 1027 | | 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 |
| | 0 | 1033 | | 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 | | |
| | 12261 | 1038 | | 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 | | |
| | 0 | 1043 | | 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 | | |
| | 0 | 1050 | | private static Encoding BigEndianUTF32 => UTF32Encoding.s_bigEndianDefault; |
| | | 1051 | | |
| | | 1052 | | public override bool Equals([NotNullWhen(true)] object? value) => |
| | 0 | 1053 | | value is Encoding that && |
| | 0 | 1054 | | (_codePage == that._codePage) && |
| | 0 | 1055 | | (EncoderFallback.Equals(that.EncoderFallback)) && |
| | 0 | 1056 | | (DecoderFallback.Equals(that.DecoderFallback)); |
| | | 1057 | | |
| | | 1058 | | public override int GetHashCode() => |
| | 0 | 1059 | | _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 | | { |
| | 0 | 1081 | | ArgumentNullException.ThrowIfNull(innerStream); |
| | 0 | 1082 | | ArgumentNullException.ThrowIfNull(innerStreamEncoding); |
| | 0 | 1083 | | 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 | | |
| | 0 | 1091 | | 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 |
| | 0 | 1098 | | throw new ArgumentException( |
| | 0 | 1099 | | SR.Format(SR.Argument_EncodingConversionOverflowBytes, _codePage, EncoderFallback.GetType()), "bytes"); |
| | | 1100 | | |
| | | 1101 | | internal void ThrowBytesOverflow(EncoderNLS? encoder, bool nothingEncoded) |
| | | 1102 | | { |
| | 0 | 1103 | | if (encoder is null || encoder._throwOnOverflow || nothingEncoded) |
| | | 1104 | | { |
| | 0 | 1105 | | if (encoder is not null && encoder.InternalHasFallbackBuffer) |
| | 0 | 1106 | | 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 |
| | 0 | 1109 | | ThrowBytesOverflow(); |
| | | 1110 | | } |
| | | 1111 | | |
| | | 1112 | | // If we didn't throw, we are in convert and have to remember our flushing |
| | 0 | 1113 | | encoder.ClearMustFlush(); |
| | 0 | 1114 | | } |
| | | 1115 | | |
| | | 1116 | | [DoesNotReturn] |
| | | 1117 | | [StackTraceHidden] |
| | | 1118 | | internal static void ThrowConversionOverflow() => |
| | 0 | 1119 | | 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 |
| | 0 | 1126 | | throw new ArgumentException( |
| | 0 | 1127 | | SR.Format(SR.Argument_EncodingConversionOverflowChars, _codePage, DecoderFallback.GetType()), "chars"); |
| | | 1128 | | |
| | | 1129 | | internal void ThrowCharsOverflow(DecoderNLS? decoder, bool nothingDecoded) |
| | | 1130 | | { |
| | 0 | 1131 | | if (decoder is null || decoder._throwOnOverflow || nothingDecoded) |
| | | 1132 | | { |
| | 0 | 1133 | | if (decoder is not null && decoder.InternalHasFallbackBuffer) |
| | 0 | 1134 | | 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 |
| | 0 | 1138 | | ThrowCharsOverflow(); |
| | | 1139 | | } |
| | | 1140 | | |
| | | 1141 | | // If we didn't throw, we are in convert and have to remember our flushing |
| | 0 | 1142 | | decoder.ClearMustFlush(); |
| | 0 | 1143 | | } |
| | | 1144 | | |
| | | 1145 | | internal sealed class DefaultEncoder : Encoder |
| | | 1146 | | { |
| | | 1147 | | private readonly Encoding _encoding; |
| | | 1148 | | |
| | 0 | 1149 | | public DefaultEncoder(Encoding encoding) |
| | | 1150 | | { |
| | 0 | 1151 | | _encoding = encoding; |
| | 0 | 1152 | | } |
| | | 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) => |
| | 0 | 1163 | | _encoding.GetByteCount(chars, index, count); |
| | | 1164 | | |
| | | 1165 | | public override unsafe int GetByteCount(char* chars, int count, bool flush) => |
| | 0 | 1166 | | _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) => |
| | 0 | 1190 | | _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) => |
| | 0 | 1194 | | _encoding.GetBytes(chars, charCount, bytes, byteCount); |
| | | 1195 | | } |
| | | 1196 | | |
| | | 1197 | | internal sealed class DefaultDecoder : Decoder |
| | | 1198 | | { |
| | | 1199 | | private readonly Encoding _encoding; |
| | | 1200 | | |
| | 0 | 1201 | | public DefaultDecoder(Encoding encoding) |
| | | 1202 | | { |
| | 0 | 1203 | | _encoding = encoding; |
| | 0 | 1204 | | } |
| | | 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) => |
| | 0 | 1214 | | GetCharCount(bytes, index, count, false); |
| | | 1215 | | |
| | | 1216 | | public override int GetCharCount(byte[] bytes, int index, int count, bool flush) => |
| | 0 | 1217 | | _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 |
| | 0 | 1221 | | _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) => |
| | 0 | 1242 | | 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) => |
| | 0 | 1246 | | _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 |
| | 0 | 1251 | | _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 | | |
| | 0 | 1267 | | internal unsafe EncodingCharBuffer(Encoding enc, DecoderNLS? decoder, char* charStart, int charCount, |
| | 0 | 1268 | | byte* byteStart, int byteCount) |
| | | 1269 | | { |
| | 0 | 1270 | | _enc = enc; |
| | 0 | 1271 | | _decoder = decoder; |
| | | 1272 | | |
| | 0 | 1273 | | _chars = charStart; |
| | 0 | 1274 | | _charStart = charStart; |
| | 0 | 1275 | | _charEnd = charStart + charCount; |
| | | 1276 | | |
| | 0 | 1277 | | _byteStart = byteStart; |
| | 0 | 1278 | | _bytes = byteStart; |
| | 0 | 1279 | | _byteEnd = byteStart + byteCount; |
| | | 1280 | | |
| | 0 | 1281 | | _fallbackBuffer = _decoder is null ? |
| | 0 | 1282 | | enc.DecoderFallback.CreateFallbackBuffer() : |
| | 0 | 1283 | | _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) |
| | 0 | 1287 | | Debug.Assert(_fallbackBuffer.Remaining == 0, |
| | 0 | 1288 | | "[Encoding.EncodingCharBuffer.EncodingCharBuffer]Expected empty fallback buffer for getchars/charcou |
| | 0 | 1289 | | _fallbackBuffer.InternalInitialize(_bytes, _charEnd); |
| | 0 | 1290 | | } |
| | | 1291 | | |
| | | 1292 | | internal unsafe bool AddChar(char ch, int numBytes) |
| | | 1293 | | { |
| | 0 | 1294 | | if (_chars is not null) |
| | | 1295 | | { |
| | 0 | 1296 | | if (_chars >= _charEnd) |
| | | 1297 | | { |
| | | 1298 | | // Throw maybe |
| | 0 | 1299 | | _bytes -= numBytes; // Didn't encode these bytes |
| | 0 | 1300 | | _enc.ThrowCharsOverflow(_decoder, _chars == _charStart); // Throw? |
| | 0 | 1301 | | return false; // No throw, but no store either |
| | | 1302 | | } |
| | | 1303 | | |
| | 0 | 1304 | | *(_chars++) = ch; |
| | | 1305 | | } |
| | 0 | 1306 | | _charCountResult++; |
| | 0 | 1307 | | return true; |
| | | 1308 | | } |
| | | 1309 | | |
| | 0 | 1310 | | 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 | | { |
| | 0 | 1327 | | _bytes += count; |
| | 0 | 1328 | | } |
| | | 1329 | | |
| | 0 | 1330 | | 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 | | { |
| | 0 | 1339 | | Debug.Assert(_bytes < _byteEnd, "[EncodingCharBuffer.GetNextByte]Expected more date"); |
| | 0 | 1340 | | if (_bytes >= _byteEnd) |
| | 0 | 1341 | | return 0; |
| | 0 | 1342 | | return *(_bytes++); |
| | | 1343 | | } |
| | | 1344 | | |
| | 0 | 1345 | | internal unsafe int BytesUsed => (int)(_bytes - _byteStart); |
| | | 1346 | | |
| | | 1347 | | internal bool Fallback(byte fallbackByte) |
| | | 1348 | | { |
| | | 1349 | | // Build our buffer |
| | 0 | 1350 | | byte[] byteBuffer = [fallbackByte]; |
| | | 1351 | | |
| | | 1352 | | // Do the fallback and add the data. |
| | 0 | 1353 | | 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. |
| | 0 | 1377 | | if (_chars is not null) |
| | | 1378 | | { |
| | 0 | 1379 | | char* pTemp = _chars; |
| | 0 | 1380 | | if (!_fallbackBuffer.InternalFallback(byteBuffer, _bytes, ref _chars)) |
| | | 1381 | | { |
| | | 1382 | | // Throw maybe |
| | 0 | 1383 | | _bytes -= byteBuffer.Length; // Didn't use how many ever bytes we're |
| | 0 | 1384 | | _fallbackBuffer.InternalReset(); // We didn't use this fallback. |
| | 0 | 1385 | | _enc.ThrowCharsOverflow(_decoder, _chars == _charStart); // Throw? |
| | 0 | 1386 | | return false; // No throw, but no store either |
| | | 1387 | | } |
| | 0 | 1388 | | _charCountResult += unchecked((int)(_chars - pTemp)); |
| | | 1389 | | } |
| | | 1390 | | else |
| | | 1391 | | { |
| | 0 | 1392 | | _charCountResult += _fallbackBuffer.InternalFallback(byteBuffer, _bytes); |
| | | 1393 | | } |
| | | 1394 | | |
| | 0 | 1395 | | return true; |
| | | 1396 | | } |
| | | 1397 | | |
| | 0 | 1398 | | 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 | | |
| | 0 | 1414 | | internal unsafe EncodingByteBuffer(Encoding inEncoding, EncoderNLS? inEncoder, |
| | 0 | 1415 | | byte* inByteStart, int inByteCount, char* inCharStart, int inCharCount) |
| | | 1416 | | { |
| | 0 | 1417 | | _enc = inEncoding; |
| | 0 | 1418 | | _encoder = inEncoder; |
| | | 1419 | | |
| | 0 | 1420 | | _charStart = inCharStart; |
| | 0 | 1421 | | _chars = inCharStart; |
| | 0 | 1422 | | _charEnd = inCharStart + inCharCount; |
| | | 1423 | | |
| | 0 | 1424 | | _bytes = inByteStart; |
| | 0 | 1425 | | _byteStart = inByteStart; |
| | 0 | 1426 | | _byteEnd = inByteStart + inByteCount; |
| | | 1427 | | |
| | 0 | 1428 | | if (_encoder is null) |
| | | 1429 | | { |
| | 0 | 1430 | | this.fallbackBuffer = _enc.EncoderFallback.CreateFallbackBuffer(); |
| | | 1431 | | } |
| | | 1432 | | else |
| | | 1433 | | { |
| | 0 | 1434 | | this.fallbackBuffer = _encoder.FallbackBuffer; |
| | | 1435 | | // If we're not converting we must not have data in our fallback buffer |
| | 0 | 1436 | | if (_encoder._throwOnOverflow && _encoder.InternalHasFallbackBuffer && |
| | 0 | 1437 | | this.fallbackBuffer.Remaining > 0) |
| | 0 | 1438 | | throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, |
| | 0 | 1439 | | _encoder.Encoding.EncodingName, _encoder.Fallback!.GetType())); |
| | | 1440 | | } |
| | 0 | 1441 | | fallbackBuffer.InternalInitialize(_chars, _charEnd, _encoder, _bytes is not null); |
| | 0 | 1442 | | } |
| | | 1443 | | |
| | | 1444 | | internal unsafe bool AddByte(byte b, int moreBytesExpected) |
| | | 1445 | | { |
| | 0 | 1446 | | Debug.Assert(moreBytesExpected >= 0, "[EncodingByteBuffer.AddByte]expected non-negative moreBytesExpecte |
| | 0 | 1447 | | if (_bytes is not null) |
| | | 1448 | | { |
| | 0 | 1449 | | if (_bytes >= _byteEnd - moreBytesExpected) |
| | | 1450 | | { |
| | | 1451 | | // Throw maybe. Check which buffer to back up (only matters if Converting) |
| | 0 | 1452 | | this.MovePrevious(true); // Throw if necessary |
| | 0 | 1453 | | return false; // No throw, but no store either |
| | | 1454 | | } |
| | | 1455 | | |
| | 0 | 1456 | | *(_bytes++) = b; |
| | | 1457 | | } |
| | 0 | 1458 | | _byteCountResult++; |
| | 0 | 1459 | | return true; |
| | | 1460 | | } |
| | | 1461 | | |
| | 0 | 1462 | | internal bool AddByte(byte b1) => AddByte(b1, 0); |
| | | 1463 | | |
| | 0 | 1464 | | internal bool AddByte(byte b1, byte b2) => AddByte(b1, b2, 0); |
| | | 1465 | | |
| | | 1466 | | internal bool AddByte(byte b1, byte b2, int moreBytesExpected) => |
| | 0 | 1467 | | 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 | | { |
| | 0 | 1484 | | if (fallbackBuffer.bFallingBack) |
| | 0 | 1485 | | fallbackBuffer.MovePrevious(); // don't use last fallback |
| | | 1486 | | else |
| | | 1487 | | { |
| | 0 | 1488 | | Debug.Assert(_chars > _charStart || |
| | 0 | 1489 | | (bThrow && (_bytes == _byteStart)), |
| | 0 | 1490 | | "[EncodingByteBuffer.MovePrevious]expected previous data or throw"); |
| | 0 | 1491 | | if (_chars > _charStart) |
| | 0 | 1492 | | _chars--; // don't use last char |
| | | 1493 | | } |
| | | 1494 | | |
| | 0 | 1495 | | if (bThrow) |
| | 0 | 1496 | | _enc.ThrowBytesOverflow(_encoder, _bytes == _byteStart); // Throw? (and reset fallback if not con |
| | 0 | 1497 | | } |
| | | 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. |
| | 0 | 1507 | | (fallbackBuffer.Remaining > 0) || (_chars < _charEnd); |
| | | 1508 | | |
| | | 1509 | | internal unsafe char GetNextChar() |
| | | 1510 | | { |
| | | 1511 | | // See if there's something in our fallback buffer |
| | 0 | 1512 | | char cReturn = fallbackBuffer.InternalGetNextChar(); |
| | | 1513 | | |
| | | 1514 | | // Nothing in the fallback buffer, return our normal data. |
| | 0 | 1515 | | if (cReturn == 0) |
| | | 1516 | | { |
| | 0 | 1517 | | if (_chars < _charEnd) |
| | 0 | 1518 | | cReturn = *(_chars++); |
| | | 1519 | | } |
| | | 1520 | | |
| | 0 | 1521 | | return cReturn; |
| | | 1522 | | } |
| | | 1523 | | |
| | 0 | 1524 | | internal unsafe int CharsUsed => (int)(_chars - _charStart); |
| | | 1525 | | |
| | 0 | 1526 | | internal int Count => _byteCountResult; |
| | | 1527 | | } |
| | | 1528 | | } |
| | | 1529 | | } |
| | | 1530 | | |