| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- //
- // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
- //
- // This define can be used to turn off the fast loops. Useful for finding whether
- // the problem is fastloop-specific.
- #define FASTLOOP
- using System;
- using System.Globalization;
- using System.Diagnostics;
- using System.Runtime.InteropServices;
- using Internal.Runtime.CompilerServices;
- namespace System.Text
- {
- public class UnicodeEncoding : Encoding
- {
- // Used by Encoding.BigEndianUnicode/Unicode for lazy initialization
- // The initialization code will not be run until a static member of the class is referenced
- internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true);
- internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true);
- private readonly bool isThrowException = false;
- private readonly bool bigEndian = false;
- private readonly bool byteOrderMark = false;
- // Unicode version 2.0 character size in bytes
- public const int CharSize = 2;
- public UnicodeEncoding()
- : this(false, true)
- {
- }
- public UnicodeEncoding(bool bigEndian, bool byteOrderMark)
- : base(bigEndian ? 1201 : 1200) //Set the data item.
- {
- this.bigEndian = bigEndian;
- this.byteOrderMark = byteOrderMark;
- }
- public UnicodeEncoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidBytes)
- : this(bigEndian, byteOrderMark)
- {
- this.isThrowException = throwOnInvalidBytes;
- // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions
- if (this.isThrowException)
- SetDefaultFallbacks();
- }
- internal sealed override void SetDefaultFallbacks()
- {
- // For UTF-X encodings, we use a replacement fallback with an empty string
- if (this.isThrowException)
- {
- this.encoderFallback = EncoderFallback.ExceptionFallback;
- this.decoderFallback = DecoderFallback.ExceptionFallback;
- }
- else
- {
- this.encoderFallback = new EncoderReplacementFallback("\xFFFD");
- this.decoderFallback = new DecoderReplacementFallback("\xFFFD");
- }
- }
- // The following methods are copied from EncodingNLS.cs.
- // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
- // These should be kept in sync for the following classes:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- //
- // Returns the number of bytes required to encode a range of characters in
- // a character array.
- //
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe int GetByteCount(char[] chars, int index, int count)
- {
- // Validate input parameters
- if (chars == null)
- throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array);
- if (index < 0 || count < 0)
- throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (chars.Length - index < count)
- throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
- // If no input, return 0, avoid fixed empty array problem
- if (count == 0)
- return 0;
- // Just call the pointer version
- fixed (char* pChars = chars)
- return GetByteCount(pChars + index, count, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe int GetByteCount(string s)
- {
- // Validate input
- if (s==null)
- throw new ArgumentNullException(nameof(s));
- fixed (char* pChars = s)
- return GetByteCount(pChars, s.Length, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- [CLSCompliant(false)]
- public override unsafe int GetByteCount(char* chars, int count)
- {
- // Validate Parameters
- if (chars == null)
- throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- // Call it with empty encoder
- return GetByteCount(chars, count, null);
- }
- // Parent method is safe.
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- public override unsafe int GetBytes(string s, int charIndex, int charCount,
- byte[] bytes, int byteIndex)
- {
- if (s == null || bytes == null)
- throw new ArgumentNullException((s == null ? nameof(s) : nameof(bytes)), SR.ArgumentNull_Array);
- if (charIndex < 0 || charCount < 0)
- throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (s.Length - charIndex < charCount)
- throw new ArgumentOutOfRangeException(nameof(s), SR.ArgumentOutOfRange_IndexCount);
- if (byteIndex < 0 || byteIndex > bytes.Length)
- throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index);
- int byteCount = bytes.Length - byteIndex;
- fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span<byte>)bytes))
- return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
- }
- // Encodes a range of characters in a character array into a range of bytes
- // in a byte array. An exception occurs if the byte array is not large
- // enough to hold the complete encoding of the characters. The
- // GetByteCount method can be used to determine the exact number of
- // bytes that will be produced for a given range of characters.
- // Alternatively, the GetMaxByteCount method can be used to
- // determine the maximum number of bytes that will be produced for a given
- // number of characters, regardless of the actual character values.
- //
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
- byte[] bytes, int byteIndex)
- {
- // Validate parameters
- if (chars == null || bytes == null)
- throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), SR.ArgumentNull_Array);
- if (charIndex < 0 || charCount < 0)
- throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (chars.Length - charIndex < charCount)
- throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
- if (byteIndex < 0 || byteIndex > bytes.Length)
- throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index);
- // If nothing to encode return 0, avoid fixed problem
- if (charCount == 0)
- return 0;
- // Just call pointer version
- int byteCount = bytes.Length - byteIndex;
- fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span<byte>)bytes))
- // Remember that byteCount is # to decode, not size of array.
- return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- [CLSCompliant(false)]
- public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
- {
- // Validate Parameters
- if (bytes == null || chars == null)
- throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array);
- if (charCount < 0 || byteCount < 0)
- throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
- return GetBytes(chars, charCount, bytes, byteCount, null);
- }
- // Returns the number of characters produced by decoding a range of bytes
- // in a byte array.
- //
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe int GetCharCount(byte[] bytes, int index, int count)
- {
- // Validate Parameters
- if (bytes == null)
- throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
- if (index < 0 || count < 0)
- throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (bytes.Length - index < count)
- throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
- // If no input just return 0, fixed doesn't like 0 length arrays
- if (count == 0)
- return 0;
- // Just call pointer version
- fixed (byte* pBytes = bytes)
- return GetCharCount(pBytes + index, count, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- [CLSCompliant(false)]
- public override unsafe int GetCharCount(byte* bytes, int count)
- {
- // Validate Parameters
- if (bytes == null)
- throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- return GetCharCount(bytes, count, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
- char[] chars, int charIndex)
- {
- // Validate Parameters
- if (bytes == null || chars == null)
- throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array);
- if (byteIndex < 0 || byteCount < 0)
- throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if ( bytes.Length - byteIndex < byteCount)
- throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
- if (charIndex < 0 || charIndex > chars.Length)
- throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index);
- // If no input, return 0 & avoid fixed problem
- if (byteCount == 0)
- return 0;
- // Just call pointer version
- int charCount = chars.Length - charIndex;
- fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span<char>)chars))
- // Remember that charCount is # to decode, not size of array
- return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
- }
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- [CLSCompliant(false)]
- public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
- {
- // Validate Parameters
- if (bytes == null || chars == null)
- throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), SR.ArgumentNull_Array);
- if (charCount < 0 || byteCount < 0)
- throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
- return GetChars(bytes, byteCount, chars, charCount, null);
- }
- // Returns a string containing the decoded representation of a range of
- // bytes in a byte array.
- //
- // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
- // So if you fix this, fix the others. Currently those include:
- // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
- // parent method is safe
- public override unsafe string GetString(byte[] bytes, int index, int count)
- {
- // Validate Parameters
- if (bytes == null)
- throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
- if (index < 0 || count < 0)
- throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (bytes.Length - index < count)
- throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
- // Avoid problems with empty input buffer
- if (count == 0) return string.Empty;
- fixed (byte* pBytes = bytes)
- return string.CreateStringFromEncoding(
- pBytes + index, count, this);
- }
- //
- // End of standard methods copied from EncodingNLS.cs
- //
- internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder)
- {
- Debug.Assert(chars != null, "[UnicodeEncoding.GetByteCount]chars!=null");
- Debug.Assert(count >= 0, "[UnicodeEncoding.GetByteCount]count >=0");
- // Start by assuming each char gets 2 bytes
- int byteCount = count << 1;
- // Check for overflow in byteCount
- // (If they were all invalid chars, this would actually be wrong,
- // but that's a ridiculously large # so we're not concerned about that case)
- if (byteCount < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow);
- char* charStart = chars;
- char* charEnd = chars + count;
- char charLeftOver = (char)0;
- bool wasHereBefore = false;
- // For fallback we may need a fallback buffer
- EncoderFallbackBuffer fallbackBuffer = null;
- char* charsForFallback;
- if (encoder != null)
- {
- charLeftOver = encoder._charLeftOver;
- // Assume extra bytes to encode charLeftOver if it existed
- if (charLeftOver > 0)
- byteCount += 2;
- // We mustn't have left over fallback data when counting
- if (encoder.InternalHasFallbackBuffer)
- {
- fallbackBuffer = encoder.FallbackBuffer;
- if (fallbackBuffer.Remaining > 0)
- throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- }
- char ch;
- TryAgain:
- while (((ch = (fallbackBuffer == null) ? (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) || chars < charEnd)
- {
- // First unwind any fallback
- if (ch == 0)
- {
- // No fallback, maybe we can do it fast
- #if FASTLOOP
- // If endianess is backwards then each pair of bytes would be backwards.
- if ( (bigEndian ^ BitConverter.IsLittleEndian) &&
- #if BIT64
- (unchecked((long)chars) & 7) == 0 &&
- #else
- (unchecked((int)chars) & 3) == 0 &&
- #endif
- charLeftOver == 0)
- {
- // Need -1 to check 2 at a time. If we have an even #, longChars will go
- // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
- // will go from longEnd - 1 long to longEnd. (Might not get to use this)
- ulong* longEnd = (ulong*)(charEnd - 3);
- // Need new char* so we can check 4 at a time
- ulong* longChars = (ulong*)chars;
- while (longChars < longEnd)
- {
- // See if we potentially have surrogates (0x8000 bit set)
- // (We're either big endian on a big endian machine or little endian on
- // a little endian machine so that'll work)
- if ((0x8000800080008000 & *longChars) != 0)
- {
- // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
- // 5 bits looks like 11011, then its a high or low surrogate.
- // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
- // Note that we expect BMP characters to be more common than surrogates
- // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
- ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
- // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
- // but no clue if they're high or low.
- // If each of the 4 characters are non-zero, then none are surrogates.
- if ((uTemp & 0xFFFF000000000000) == 0 ||
- (uTemp & 0x0000FFFF00000000) == 0 ||
- (uTemp & 0x00000000FFFF0000) == 0 ||
- (uTemp & 0x000000000000FFFF) == 0)
- {
- // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
- // or if there's 1 or 4 surrogates
- // If they happen to be high/low/high/low, we may as well continue. Check the next
- // bit to see if its set (low) or not (high) in the right pattern
- if ((0xfc00fc00fc00fc00 & *longChars) !=
- (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00))
- {
- // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
- // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
- // Drop out to the slow loop to resolve the surrogates
- break;
- }
- // else they are all surrogates in High/Low/High/Low order, so we can use them.
- }
- // else none are surrogates, so we can use them.
- }
- // else all < 0x8000 so we can use them
- // We already counted these four chars, go to next long.
- longChars++;
- }
- chars = (char*)longChars;
- if (chars >= charEnd)
- break;
- }
- #endif // FASTLOOP
- // No fallback, just get next char
- ch = *chars;
- chars++;
- }
- else
- {
- // We weren't preallocating fallback space.
- byteCount += 2;
- }
- // Check for high or low surrogates
- if (ch >= 0xd800 && ch <= 0xdfff)
- {
- // Was it a high surrogate?
- if (ch <= 0xdbff)
- {
- // Its a high surrogate, if we already had a high surrogate do its fallback
- if (charLeftOver > 0)
- {
- // Unwind the current character, this should be safe because we
- // don't have leftover data in the fallback, so chars must have
- // advanced already.
- Debug.Assert(chars > charStart,
- "[UnicodeEncoding.GetByteCount]Expected chars to have advanced in unexpected high surrogate");
- chars--;
- // If previous high surrogate deallocate 2 bytes
- byteCount -= 2;
- // Fallback the previous surrogate
- // Need to initialize fallback buffer?
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- // Now no high surrogate left over
- charLeftOver = (char)0;
- continue;
- }
- // Remember this high surrogate
- charLeftOver = ch;
- continue;
- }
- // Its a low surrogate
- if (charLeftOver == 0)
- {
- // Expected a previous high surrogate.
- // Don't count this one (we'll count its fallback if necessary)
- byteCount -= 2;
- // fallback this one
- // Need to initialize fallback buffer?
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(ch, ref charsForFallback);
- chars = charsForFallback;
- continue;
- }
- // Valid surrogate pair, add our charLeftOver
- charLeftOver = (char)0;
- continue;
- }
- else if (charLeftOver > 0)
- {
- // Expected a low surrogate, but this char is normal
- // Rewind the current character, fallback previous character.
- // this should be safe because we don't have leftover data in the
- // fallback, so chars must have advanced already.
- Debug.Assert(chars > charStart,
- "[UnicodeEncoding.GetByteCount]Expected chars to have advanced when expected low surrogate");
- chars--;
- // fallback previous chars
- // Need to initialize fallback buffer?
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- // Ignore charLeftOver or throw
- byteCount -= 2;
- charLeftOver = (char)0;
- continue;
- }
- // Ok we had something to add (already counted)
- }
- // Don't allocate space for left over char
- if (charLeftOver > 0)
- {
- byteCount -= 2;
- // If we have to flush, stick it in fallback and try again
- if (encoder == null || encoder.MustFlush)
- {
- if (wasHereBefore)
- {
- // Throw it, using our complete character
- throw new ArgumentException(
- SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
- }
- else
- {
- // Need to initialize fallback buffer?
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- charLeftOver = (char)0;
- wasHereBefore = true;
- goto TryAgain;
- }
- }
- }
- // Shouldn't have anything in fallback buffer for GetByteCount
- // (don't have to check _throwOnOverflow for count)
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
- "[UnicodeEncoding.GetByteCount]Expected empty fallback buffer at end");
- // Don't remember fallbackBuffer.encoder for counting
- return byteCount;
- }
- internal sealed override unsafe int GetBytes(
- char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder)
- {
- Debug.Assert(chars != null, "[UnicodeEncoding.GetBytes]chars!=null");
- Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetBytes]byteCount >=0");
- Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetBytes]charCount >=0");
- Debug.Assert(bytes != null, "[UnicodeEncoding.GetBytes]bytes!=null");
- char charLeftOver = (char)0;
- char ch;
- bool wasHereBefore = false;
- byte* byteEnd = bytes + byteCount;
- char* charEnd = chars + charCount;
- byte* byteStart = bytes;
- char* charStart = chars;
- // For fallback we may need a fallback buffer
- EncoderFallbackBuffer fallbackBuffer = null;
- char* charsForFallback;
- // Get our encoder, but don't clear it yet.
- if (encoder != null)
- {
- charLeftOver = encoder._charLeftOver;
- // We mustn't have left over fallback data when counting
- if (encoder.InternalHasFallbackBuffer)
- {
- // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
- fallbackBuffer = encoder.FallbackBuffer;
- if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow)
- throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
- }
- }
- TryAgain:
- while (((ch = (fallbackBuffer == null) ?
- (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) ||
- chars < charEnd)
- {
- // First unwind any fallback
- if (ch == 0)
- {
- // No fallback, maybe we can do it fast
- #if FASTLOOP
- // If endianess is backwards then each pair of bytes would be backwards.
- if ( (bigEndian ^ BitConverter.IsLittleEndian) &&
- #if BIT64
- (unchecked((long)chars) & 7) == 0 &&
- #else
- (unchecked((int)chars) & 3) == 0 &&
- #endif
- charLeftOver == 0)
- {
- // Need -1 to check 2 at a time. If we have an even #, longChars will go
- // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
- // will go from longEnd - 1 long to longEnd. (Might not get to use this)
- // We can only go iCount units (limited by shorter of char or byte buffers.
- ulong* longEnd = (ulong*)(chars - 3 +
- (((byteEnd - bytes) >> 1 < charEnd - chars) ?
- (byteEnd - bytes) >> 1 : charEnd - chars));
- // Need new char* so we can check 4 at a time
- ulong* longChars = (ulong*)chars;
- ulong* longBytes = (ulong*)bytes;
- while (longChars < longEnd)
- {
- // See if we potentially have surrogates (0x8000 bit set)
- // (We're either big endian on a big endian machine or little endian on
- // a little endian machine so that'll work)
- if ((0x8000800080008000 & *longChars) != 0)
- {
- // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
- // 5 bits looks like 11011, then its a high or low surrogate.
- // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
- // Note that we expect BMP characters to be more common than surrogates
- // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
- ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
- // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
- // but no clue if they're high or low.
- // If each of the 4 characters are non-zero, then none are surrogates.
- if ((uTemp & 0xFFFF000000000000) == 0 ||
- (uTemp & 0x0000FFFF00000000) == 0 ||
- (uTemp & 0x00000000FFFF0000) == 0 ||
- (uTemp & 0x000000000000FFFF) == 0)
- {
- // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
- // or if there's 1 or 4 surrogates
- // If they happen to be high/low/high/low, we may as well continue. Check the next
- // bit to see if its set (low) or not (high) in the right pattern
- if ((0xfc00fc00fc00fc00 & *longChars) !=
- (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00))
- {
- // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
- // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
- // Drop out to the slow loop to resolve the surrogates
- break;
- }
- // else they are all surrogates in High/Low/High/Low order, so we can use them.
- }
- // else none are surrogates, so we can use them.
- }
- // else all < 0x8000 so we can use them
- // We can use these 4 chars.
- Unsafe.WriteUnaligned<ulong>(longBytes, *longChars);
- longChars++;
- longBytes++;
- }
- chars = (char*)longChars;
- bytes = (byte*)longBytes;
- if (chars >= charEnd)
- break;
- }
- #endif // FASTLOOP
- // No fallback, just get next char
- ch = *chars;
- chars++;
- }
- // Check for high or low surrogates
- if (ch >= 0xd800 && ch <= 0xdfff)
- {
- // Was it a high surrogate?
- if (ch <= 0xdbff)
- {
- // Its a high surrogate, see if we already had a high surrogate
- if (charLeftOver > 0)
- {
- // Unwind the current character, this should be safe because we
- // don't have leftover data in the fallback, so chars must have
- // advanced already.
- Debug.Assert(chars > charStart,
- "[UnicodeEncoding.GetBytes]Expected chars to have advanced in unexpected high surrogate");
- chars--;
- // Fallback the previous surrogate
- // Might need to create our fallback buffer
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- charLeftOver = (char)0;
- continue;
- }
- // Remember this high surrogate
- charLeftOver = ch;
- continue;
- }
- // Its a low surrogate
- if (charLeftOver == 0)
- {
- // We'll fall back this one
- // Might need to create our fallback buffer
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(ch, ref charsForFallback);
- chars = charsForFallback;
- continue;
- }
- // Valid surrogate pair, add our charLeftOver
- if (bytes + 3 >= byteEnd)
- {
- // Not enough room to add this surrogate pair
- if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
- {
- // These must have both been from the fallbacks.
- // Both of these MUST have been from a fallback because if the 1st wasn't
- // from a fallback, then a high surrogate followed by an illegal char
- // would've caused the high surrogate to fall back. If a high surrogate
- // fell back, then it was consumed and both chars came from the fallback.
- fallbackBuffer.MovePrevious(); // Didn't use either fallback surrogate
- fallbackBuffer.MovePrevious();
- }
- else
- {
- // If we don't have enough room, then either we should've advanced a while
- // or we should have bytes==byteStart and throw below
- Debug.Assert(chars > charStart + 1 || bytes == byteStart,
- "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair");
- chars -= 2; // Didn't use either surrogate
- }
- ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
- charLeftOver = (char)0; // we'll retry it later
- break; // Didn't throw, but stop 'til next time.
- }
- if (bigEndian)
- {
- *(bytes++) = (byte)(charLeftOver >> 8);
- *(bytes++) = (byte)charLeftOver;
- }
- else
- {
- *(bytes++) = (byte)charLeftOver;
- *(bytes++) = (byte)(charLeftOver >> 8);
- }
- charLeftOver = (char)0;
- }
- else if (charLeftOver > 0)
- {
- // Expected a low surrogate, but this char is normal
- // Rewind the current character, fallback previous character.
- // this should be safe because we don't have leftover data in the
- // fallback, so chars must have advanced already.
- Debug.Assert(chars > charStart,
- "[UnicodeEncoding.GetBytes]Expected chars to have advanced after expecting low surrogate");
- chars--;
- // fallback previous chars
- // Might need to create our fallback buffer
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- // Ignore charLeftOver or throw
- charLeftOver = (char)0;
- continue;
- }
- // Ok, we have a char to add
- if (bytes + 1 >= byteEnd)
- {
- // Couldn't add this char
- if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
- fallbackBuffer.MovePrevious(); // Not using this fallback char
- else
- {
- // Lonely charLeftOver (from previous call) would've been caught up above,
- // so this must be a case where we've already read an input char.
- Debug.Assert(chars > charStart,
- "[UnicodeEncoding.GetBytes]Expected chars to have advanced for failed fallback");
- chars--; // Not using this char
- }
- ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
- break; // didn't throw, just stop
- }
- if (bigEndian)
- {
- *(bytes++) = (byte)(ch >> 8);
- *(bytes++) = (byte)ch;
- }
- else
- {
- *(bytes++) = (byte)ch;
- *(bytes++) = (byte)(ch >> 8);
- }
- }
- // Don't allocate space for left over char
- if (charLeftOver > 0)
- {
- // If we aren't flushing we need to fall this back
- if (encoder == null || encoder.MustFlush)
- {
- if (wasHereBefore)
- {
- // Throw it, using our complete character
- throw new ArgumentException(
- SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
- }
- else
- {
- // If we have to flush, stick it in fallback and try again
- // Might need to create our fallback buffer
- if (fallbackBuffer == null)
- {
- if (encoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = encoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
- }
- // If we're not flushing, that'll remember the left over character.
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
- chars = charsForFallback;
- charLeftOver = (char)0;
- wasHereBefore = true;
- goto TryAgain;
- }
- }
- }
- // Not flushing, remember it in the encoder
- if (encoder != null)
- {
- encoder._charLeftOver = charLeftOver;
- encoder._charsUsed = (int)(chars - charStart);
- }
- // Remember charLeftOver if we must, or clear it if we're flushing
- // (charLeftOver should be 0 if we're flushing)
- Debug.Assert((encoder != null && !encoder.MustFlush) || charLeftOver == (char)0,
- "[UnicodeEncoding.GetBytes] Expected no left over characters if flushing");
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
- encoder == null || !encoder._throwOnOverflow,
- "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting");
- return (int)(bytes - byteStart);
- }
- internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
- {
- Debug.Assert(bytes != null, "[UnicodeEncoding.GetCharCount]bytes!=null");
- Debug.Assert(count >= 0, "[UnicodeEncoding.GetCharCount]count >=0");
- UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder;
- byte* byteEnd = bytes + count;
- byte* byteStart = bytes;
- // Need last vars
- int lastByte = -1;
- char lastChar = (char)0;
- // Start by assuming same # of chars as bytes
- int charCount = count >> 1;
- // For fallback we may need a fallback buffer
- DecoderFallbackBuffer fallbackBuffer = null;
- if (decoder != null)
- {
- lastByte = decoder.lastByte;
- lastChar = decoder.lastChar;
- // Assume extra char if last char was around
- if (lastChar > 0)
- charCount++;
- // Assume extra char if extra last byte makes up odd # of input bytes
- if (lastByte >= 0 && (count & 1) == 1)
- {
- charCount++;
- }
- // Shouldn't have anything in fallback buffer for GetCharCount
- // (don't have to check _throwOnOverflow for count)
- Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
- "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at start");
- }
- while (bytes < byteEnd)
- {
- // If we're aligned then maybe we can do it fast
- // That'll hurt if we're unaligned because we'll always test but never be aligned
- #if FASTLOOP
- if ((bigEndian ^ BitConverter.IsLittleEndian) &&
- #if BIT64
- (unchecked((long)bytes) & 7) == 0 &&
- #else
- (unchecked((int)bytes) & 3) == 0 &&
- #endif // BIT64
- lastByte == -1 && lastChar == 0)
- {
- // Need -1 to check 2 at a time. If we have an even #, longBytes will go
- // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longBytes
- // will go from longEnd - 1 long to longEnd. (Might not get to use this)
- ulong* longEnd = (ulong*)(byteEnd - 7);
- // Need new char* so we can check 4 at a time
- ulong* longBytes = (ulong*)bytes;
- while (longBytes < longEnd)
- {
- // See if we potentially have surrogates (0x8000 bit set)
- // (We're either big endian on a big endian machine or little endian on
- // a little endian machine so that'll work)
- if ((0x8000800080008000 & *longBytes) != 0)
- {
- // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
- // 5 bits looks like 11011, then its a high or low surrogate.
- // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
- // Note that we expect BMP characters to be more common than surrogates
- // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
- ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
- // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
- // but no clue if they're high or low.
- // If each of the 4 characters are non-zero, then none are surrogates.
- if ((uTemp & 0xFFFF000000000000) == 0 ||
- (uTemp & 0x0000FFFF00000000) == 0 ||
- (uTemp & 0x00000000FFFF0000) == 0 ||
- (uTemp & 0x000000000000FFFF) == 0)
- {
- // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
- // or if there's 1 or 4 surrogates
- // If they happen to be high/low/high/low, we may as well continue. Check the next
- // bit to see if its set (low) or not (high) in the right pattern
- if ((0xfc00fc00fc00fc00 & *longBytes) !=
- (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00))
- {
- // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
- // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
- // Drop out to the slow loop to resolve the surrogates
- break;
- }
- // else they are all surrogates in High/Low/High/Low order, so we can use them.
- }
- // else none are surrogates, so we can use them.
- }
- // else all < 0x8000 so we can use them
- // We can use these 4 chars.
- longBytes++;
- }
- bytes = (byte*)longBytes;
- if (bytes >= byteEnd)
- break;
- }
- #endif // FASTLOOP
- // Get 1st byte
- if (lastByte < 0)
- {
- lastByte = *bytes++;
- if (bytes >= byteEnd) break;
- }
- // Get full char
- char ch;
- if (bigEndian)
- {
- ch = (char)(lastByte << 8 | *(bytes++));
- }
- else
- {
- ch = (char)(*(bytes++) << 8 | lastByte);
- }
- lastByte = -1;
- // See if the char's valid
- if (ch >= 0xd800 && ch <= 0xdfff)
- {
- // Was it a high surrogate?
- if (ch <= 0xdbff)
- {
- // Its a high surrogate, if we had one then do fallback for previous one
- if (lastChar > 0)
- {
- // Ignore previous bad high surrogate
- charCount--;
- // Get fallback for previous high surrogate
- // Note we have to reconstruct bytes because some may have been in decoder
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, null);
- }
- // Get fallback.
- charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
- }
- // Ignore the last one which fell back already,
- // and remember the new high surrogate
- lastChar = ch;
- continue;
- }
- // Its a low surrogate
- if (lastChar == 0)
- {
- // Expected a previous high surrogate
- charCount--;
- // Get fallback for this low surrogate
- // Note we have to reconstruct bytes because some may have been in decoder
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(ch >> 8)), unchecked((byte)ch) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)ch), unchecked((byte)(ch >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, null);
- }
- charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
- // Ignore this one (we already did its fallback)
- continue;
- }
- // Valid surrogate pair, already counted.
- lastChar = (char)0;
- }
- else if (lastChar > 0)
- {
- // Had a high surrogate, expected a low surrogate
- // Un-count the last high surrogate
- charCount--;
- // fall back the high surrogate.
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, null);
- }
- // Already subtracted high surrogate
- charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
- // Not left over now, clear previous high surrogate and continue to add current char
- lastChar = (char)0;
- }
- // Valid char, already counted
- }
- // Extra space if we can't use decoder
- if (decoder == null || decoder.MustFlush)
- {
- if (lastChar > 0)
- {
- // No hanging high surrogates allowed, do fallback and remove count for it
- charCount--;
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, null);
- }
- charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
- lastChar = (char)0;
- }
- if (lastByte >= 0)
- {
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, null);
- }
- // No hanging odd bytes allowed if must flush
- charCount += fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes);
- lastByte = -1;
- }
- }
- // If we had a high surrogate left over, we can't count it
- if (lastChar > 0)
- charCount--;
- // Shouldn't have anything in fallback buffer for GetCharCount
- // (don't have to check _throwOnOverflow for count)
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
- "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at end");
- return charCount;
- }
- internal sealed override unsafe int GetChars(
- byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder)
- {
- Debug.Assert(chars != null, "[UnicodeEncoding.GetChars]chars!=null");
- Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetChars]byteCount >=0");
- Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetChars]charCount >=0");
- Debug.Assert(bytes != null, "[UnicodeEncoding.GetChars]bytes!=null");
- UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder;
- // Need last vars
- int lastByte = -1;
- char lastChar = (char)0;
- // Get our decoder (but don't clear it yet)
- if (decoder != null)
- {
- lastByte = decoder.lastByte;
- lastChar = decoder.lastChar;
- // Shouldn't have anything in fallback buffer for GetChars
- // (don't have to check _throwOnOverflow for chars)
- Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
- "[UnicodeEncoding.GetChars]Expected empty fallback buffer at start");
- }
- // For fallback we may need a fallback buffer
- DecoderFallbackBuffer fallbackBuffer = null;
- char* charsForFallback;
- byte* byteEnd = bytes + byteCount;
- char* charEnd = chars + charCount;
- byte* byteStart = bytes;
- char* charStart = chars;
- while (bytes < byteEnd)
- {
- // If we're aligned then maybe we can do it fast
- // That'll hurt if we're unaligned because we'll always test but never be aligned
- #if FASTLOOP
- if ((bigEndian ^ BitConverter.IsLittleEndian) &&
- #if BIT64
- (unchecked((long)chars) & 7) == 0 &&
- #else
- (unchecked((int)chars) & 3) == 0 &&
- #endif
- lastByte == -1 && lastChar == 0)
- {
- // Need -1 to check 2 at a time. If we have an even #, longChars will go
- // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
- // will go from longEnd - 1 long to longEnd. (Might not get to use this)
- // We can only go iCount units (limited by shorter of char or byte buffers.
- ulong* longEnd = (ulong*)(bytes - 7 +
- (((byteEnd - bytes) >> 1 < charEnd - chars) ?
- (byteEnd - bytes) : (charEnd - chars) << 1));
- // Need new char* so we can check 4 at a time
- ulong* longBytes = (ulong*)bytes;
- ulong* longChars = (ulong*)chars;
- while (longBytes < longEnd)
- {
- // See if we potentially have surrogates (0x8000 bit set)
- // (We're either big endian on a big endian machine or little endian on
- // a little endian machine so that'll work)
- if ((0x8000800080008000 & *longBytes) != 0)
- {
- // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
- // 5 bits looks like 11011, then its a high or low surrogate.
- // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
- // Note that we expect BMP characters to be more common than surrogates
- // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
- ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
- // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
- // but no clue if they're high or low.
- // If each of the 4 characters are non-zero, then none are surrogates.
- if ((uTemp & 0xFFFF000000000000) == 0 ||
- (uTemp & 0x0000FFFF00000000) == 0 ||
- (uTemp & 0x00000000FFFF0000) == 0 ||
- (uTemp & 0x000000000000FFFF) == 0)
- {
- // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
- // or if there's 1 or 4 surrogates
- // If they happen to be high/low/high/low, we may as well continue. Check the next
- // bit to see if its set (low) or not (high) in the right pattern
- if ((0xfc00fc00fc00fc00 & *longBytes) !=
- (BitConverter.IsLittleEndian ? (ulong)0xdc00d800dc00d800 : (ulong)0xd800dc00d800dc00))
- {
- // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
- // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
- // Drop out to the slow loop to resolve the surrogates
- break;
- }
- // else they are all surrogates in High/Low/High/Low order, so we can use them.
- }
- // else none are surrogates, so we can use them.
- }
- // else all < 0x8000 so we can use them
- // We can use these 4 chars.
- Unsafe.WriteUnaligned<ulong>(longChars, *longBytes);
- longBytes++;
- longChars++;
- }
- chars = (char*)longChars;
- bytes = (byte*)longBytes;
- if (bytes >= byteEnd)
- break;
- }
- #endif // FASTLOOP
- // Get 1st byte
- if (lastByte < 0)
- {
- lastByte = *bytes++;
- continue;
- }
- // Get full char
- char ch;
- if (bigEndian)
- {
- ch = (char)(lastByte << 8 | *(bytes++));
- }
- else
- {
- ch = (char)(*(bytes++) << 8 | lastByte);
- }
- lastByte = -1;
- // See if the char's valid
- if (ch >= 0xd800 && ch <= 0xdfff)
- {
- // Was it a high surrogate?
- if (ch <= 0xdbff)
- {
- // Its a high surrogate, if we had one then do fallback for previous one
- if (lastChar > 0)
- {
- // Get fallback for previous high surrogate
- // Note we have to reconstruct bytes because some may have been in decoder
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, charEnd);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
- chars = charsForFallback;
- if (!fallbackResult)
- {
- // couldn't fall back lonely surrogate
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (bad surrogate)");
- bytes -= 2; // didn't use these 2 bytes
- fallbackBuffer.InternalReset();
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- break; // couldn't fallback but didn't throw
- }
- }
- // Ignore the previous high surrogate which fell back already,
- // yet remember the current high surrogate for next time.
- lastChar = ch;
- continue;
- }
- // Its a low surrogate
- if (lastChar == 0)
- {
- // Expected a previous high surrogate
- // Get fallback for this low surrogate
- // Note we have to reconstruct bytes because some may have been in decoder
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(ch >> 8)), unchecked((byte)ch) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)ch), unchecked((byte)(ch >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, charEnd);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
- chars = charsForFallback;
- if (!fallbackResult)
- {
- // couldn't fall back lonely surrogate
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (lonely surrogate)");
- bytes -= 2; // didn't use these 2 bytes
- fallbackBuffer.InternalReset();
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- break; // couldn't fallback but didn't throw
- }
- // Didn't throw, ignore this one (we already did its fallback)
- continue;
- }
- // Valid surrogate pair, add our lastChar (will need 2 chars)
- if (chars >= charEnd - 1)
- {
- // couldn't find room for this surrogate pair
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (surrogate pair)");
- bytes -= 2; // didn't use these 2 bytes
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- // Leave lastChar for next call to Convert()
- break; // couldn't fallback but didn't throw
- }
- *chars++ = lastChar;
- lastChar = (char)0;
- }
- else if (lastChar > 0)
- {
- // Had a high surrogate, expected a low surrogate, fall back the high surrogate.
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, charEnd);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
- chars = charsForFallback;
- if (!fallbackResult)
- {
- // couldn't fall back high surrogate, or char that would be next
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (no low surrogate)");
- bytes -= 2; // didn't use these 2 bytes
- fallbackBuffer.InternalReset();
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- break; // couldn't fallback but didn't throw
- }
- // Not left over now, clear previous high surrogate and continue to add current char
- lastChar = (char)0;
- }
- // Valid char, room for it?
- if (chars >= charEnd)
- {
- // 2 bytes couldn't fall back
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (normal)");
- bytes -= 2; // didn't use these bytes
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- break; // couldn't fallback but didn't throw
- }
- // add it
- *chars++ = ch;
- }
- // Remember our decoder if we must
- if (decoder == null || decoder.MustFlush)
- {
- if (lastChar > 0)
- {
- // No hanging high surrogates allowed, do fallback and remove count for it
- byte[] byteBuffer = null;
- if (bigEndian)
- {
- byteBuffer = new byte[]
- { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
- }
- else
- {
- byteBuffer = new byte[]
- { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
- }
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, charEnd);
- }
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
- chars = charsForFallback;
- if (!fallbackResult)
- {
- // 2 bytes couldn't fall back
- // We either advanced bytes or chars should == charStart and throw below
- Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
- "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (decoder)");
- bytes -= 2; // didn't use these bytes
- if (lastByte >= 0)
- bytes--; // had an extra last byte hanging around
- fallbackBuffer.InternalReset();
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- // We'll remember these in our decoder though
- bytes += 2;
- if (lastByte >= 0)
- bytes++;
- goto End;
- }
- // done with this one
- lastChar = (char)0;
- }
- if (lastByte >= 0)
- {
- if (fallbackBuffer == null)
- {
- if (decoder == null)
- fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = decoder.FallbackBuffer;
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(byteStart, charEnd);
- }
- // No hanging odd bytes allowed if must flush
- charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered
- bool fallbackResult = fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes, ref charsForFallback);
- chars = charsForFallback;
- if (!fallbackResult)
- {
- // odd byte couldn't fall back
- bytes--; // didn't use this byte
- fallbackBuffer.InternalReset();
- ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
- // didn't throw, but we'll remember it in the decoder
- bytes++;
- goto End;
- }
- // Didn't fail, clear buffer
- lastByte = -1;
- }
- }
- End:
- // Remember our decoder if we must
- if (decoder != null)
- {
- Debug.Assert((decoder.MustFlush == false) || ((lastChar == (char)0) && (lastByte == -1)),
- "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing"
- // + " " + ((int)lastChar).ToString("X4") + " " + lastByte.ToString("X2")
- );
- decoder._bytesUsed = (int)(bytes - byteStart);
- decoder.lastChar = lastChar;
- decoder.lastByte = lastByte;
- }
- // Shouldn't have anything in fallback buffer for GetChars
- // (don't have to check _throwOnOverflow for count or chars)
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
- "[UnicodeEncoding.GetChars]Expected empty fallback buffer at end");
- return (int)(chars - charStart);
- }
- public override System.Text.Encoder GetEncoder()
- {
- return new EncoderNLS(this);
- }
- public override System.Text.Decoder GetDecoder()
- {
- return new UnicodeEncoding.Decoder(this);
- }
- public override byte[] GetPreamble()
- {
- if (byteOrderMark)
- {
- // Note - we must allocate new byte[]'s here to prevent someone
- // from modifying a cached byte[].
- if (bigEndian)
- return new byte[2] { 0xfe, 0xff };
- else
- return new byte[2] { 0xff, 0xfe };
- }
- return Array.Empty<byte>();
- }
- public override ReadOnlySpan<byte> Preamble =>
- GetType() != typeof(UnicodeEncoding) ? new ReadOnlySpan<byte>(GetPreamble()) : // in case a derived UnicodeEncoding overrode GetPreamble
- !byteOrderMark ? default :
- bigEndian ? (ReadOnlySpan<byte>)new byte[2] { 0xfe, 0xff } : // uses C# compiler's optimization for static byte[] data
- (ReadOnlySpan<byte>)new byte[2] { 0xff, 0xfe };
- public override int GetMaxByteCount(int charCount)
- {
- if (charCount < 0)
- throw new ArgumentOutOfRangeException(nameof(charCount),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
- long byteCount = (long)charCount + 1;
- if (EncoderFallback.MaxCharCount > 1)
- byteCount *= EncoderFallback.MaxCharCount;
- // 2 bytes per char
- byteCount <<= 1;
- if (byteCount > 0x7fffffff)
- throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
- return (int)byteCount;
- }
- public override int GetMaxCharCount(int byteCount)
- {
- if (byteCount < 0)
- throw new ArgumentOutOfRangeException(nameof(byteCount),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- // long because byteCount could be biggest int.
- // 1 char per 2 bytes. Round up in case 1 left over in decoder.
- // Round up using &1 in case byteCount is max size
- // Might also need an extra 1 if there's a left over high surrogate in the decoder.
- long charCount = (long)(byteCount >> 1) + (byteCount & 1) + 1;
- // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizarre like that)
- if (DecoderFallback.MaxCharCount > 1)
- charCount *= DecoderFallback.MaxCharCount;
- if (charCount > 0x7fffffff)
- throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
- return (int)charCount;
- }
- public override bool Equals(object value)
- {
- if (value is UnicodeEncoding that)
- {
- //
- // Big Endian Unicode has different code page (1201) than small Endian one (1200),
- // so we still have to check _codePage here.
- //
- return (CodePage == that.CodePage) &&
- byteOrderMark == that.byteOrderMark &&
- // isThrowException == that.isThrowException && // Same as Encoder/Decoder being exception fallbacks
- bigEndian == that.bigEndian &&
- (EncoderFallback.Equals(that.EncoderFallback)) &&
- (DecoderFallback.Equals(that.DecoderFallback));
- }
- return (false);
- }
- public override int GetHashCode()
- {
- return CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
- (byteOrderMark ? 4 : 0) + (bigEndian ? 8 : 0);
- }
- private sealed class Decoder : System.Text.DecoderNLS
- {
- internal int lastByte = -1;
- internal char lastChar = '\0';
- public Decoder(UnicodeEncoding encoding) : base(encoding)
- {
- // base calls reset
- }
-
- public override void Reset()
- {
- lastByte = -1;
- lastChar = '\0';
- if (_fallbackBuffer != null)
- _fallbackBuffer.Reset();
- }
- // Anything left in our decoder?
- internal override bool HasState
- {
- get
- {
- return (this.lastByte != -1 || this.lastChar != '\0');
- }
- }
- }
- }
- }
|