#define FASTLOOP
using System;
+using System.Buffers;
using System.Diagnostics;
-using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text.Unicode;
namespace System.Text
{
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 is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars, ExceptionResource.ArgumentNull_Array);
+ }
- if (chars.Length - index < count)
- throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer);
+ if ((index | count) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- // If no input, return 0, avoid fixed empty array problem
- if (count == 0)
- return 0;
+ if (chars.Length - index < count)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.chars, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
+ }
- // Just call the pointer version
fixed (char* pChars = chars)
- return GetByteCount(pChars + index, count, null);
+ {
+ return GetByteCountCommon(pChars + index, count);
+ }
}
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
public override unsafe int GetByteCount(string chars)
{
- // Validate input
- if (chars==null)
- throw new ArgumentNullException("s");
+ // Validate input parameters
+
+ if (chars is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars);
+ }
fixed (char* pChars = chars)
- return GetByteCount(pChars, chars.Length, null);
+ {
+ return GetByteCountCommon(pChars, chars.Length);
+ }
}
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
public override unsafe int GetByteCount(char* chars, int count)
{
// Validate Parameters
+
if (chars == null)
- throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array);
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.chars);
+ }
if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- // Call it with empty encoder
- return GetByteCount(chars, count, null);
+ return GetByteCountCommon(chars, count);
}
public override unsafe int GetByteCount(ReadOnlySpan<char> chars)
{
- fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
+ // It's ok for us to pass null pointers down to the workhorse below.
+
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ {
+ return GetByteCountCommon(charsPtr, chars.Length);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe int GetByteCountCommon(char* pChars, int charCount)
+ {
+ // Common helper method for all non-EncoderNLS entry points to GetByteCount.
+ // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
+
+ Debug.Assert(charCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
+
+ // First call into the fast path.
+ // Don't bother providing a fallback mechanism; our fast path doesn't use it.
+
+ int totalByteCount = GetByteCountFast(pChars, charCount, fallback: null, out int charsConsumed);
+
+ if (charsConsumed != charCount)
+ {
+ // If there's still data remaining in the source buffer, go down the fallback path.
+ // We need to check for integer overflow since the fallback could change the required
+ // output count in unexpected ways.
+
+ totalByteCount += GetByteCountWithFallback(pChars, charCount, charsConsumed);
+ if (totalByteCount < 0)
+ {
+ ThrowConversionOverflow();
+ }
+ }
+
+ return totalByteCount;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetCharCountCommon
+ private protected sealed override unsafe int GetByteCountFast(char* pChars, int charsLength, EncoderFallback fallback, out int charsConsumed)
+ {
+ // The number of UTF-8 code units may exceed the number of UTF-16 code units,
+ // so we'll need to check for overflow before casting to Int32.
+
+ char* ptrToFirstInvalidChar = Utf16Utility.GetPointerToFirstInvalidChar(pChars, charsLength, out long utf8CodeUnitCountAdjustment, out _);
+
+ int tempCharsConsumed = (int)(ptrToFirstInvalidChar - pChars);
+ charsConsumed = tempCharsConsumed;
+
+ long totalUtf8Bytes = tempCharsConsumed + utf8CodeUnitCountAdjustment;
+ if ((ulong)totalUtf8Bytes > int.MaxValue)
{
- return GetByteCount(charsPtr, chars.Length, baseEncoder: null);
+ ThrowConversionOverflow();
}
+
+ return (int)totalUtf8Bytes;
}
// Parent method is safe.
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);
+ // Validate Parameters
+
+ if (s is null || bytes is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(
+ argument: (s is null) ? ExceptionArgument.s : ExceptionArgument.bytes,
+ resource: ExceptionResource.ArgumentNull_Array);
+ }
- if (charIndex < 0 || charCount < 0)
- throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if ((charIndex | charCount) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (charIndex < 0) ? ExceptionArgument.charIndex : ExceptionArgument.charCount,
+ resource: ExceptionResource.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);
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.s, ExceptionResource.ArgumentOutOfRange_IndexCount);
+ }
- int byteCount = bytes.Length - byteIndex;
+ if ((uint)byteIndex > bytes.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.byteIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
- fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span<byte>)bytes))
- return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ fixed (char* pChars = s)
+ fixed (byte* pBytes = bytes)
+ {
+ return GetBytesCommon(pChars + charIndex, charCount, pBytes + byteIndex, bytes.Length - byteIndex);
+ }
}
// Encodes a range of characters in a character array into a range of bytes
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 (chars is null || bytes is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(
+ argument: (chars is null) ? ExceptionArgument.chars : ExceptionArgument.bytes,
+ resource: ExceptionResource.ArgumentNull_Array);
+ }
- if (byteIndex < 0 || byteIndex > bytes.Length)
- throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index);
+ if ((charIndex | charCount) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (charIndex < 0) ? ExceptionArgument.charIndex : ExceptionArgument.charCount,
+ resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- // If nothing to encode return 0, avoid fixed problem
- if (charCount == 0)
- return 0;
+ if (chars.Length - charIndex < charCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.chars, ExceptionResource.ArgumentOutOfRange_IndexCount);
+ }
- // Just call pointer version
- int byteCount = bytes.Length - byteIndex;
+ if ((uint)byteIndex > bytes.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.byteIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
- 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);
+ fixed (char* pChars = chars)
+ fixed (byte* pBytes = bytes)
+ {
+ return GetBytesCommon(pChars + charIndex, charCount, pBytes + byteIndex, bytes.Length - byteIndex);
+ }
}
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
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);
+ if (chars == null || bytes == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(
+ argument: (chars is null) ? ExceptionArgument.chars : ExceptionArgument.bytes,
+ resource: ExceptionResource.ArgumentNull_Array);
+ }
+
+ if ((charCount | byteCount) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (charCount < 0) ? ExceptionArgument.charCount : ExceptionArgument.byteCount,
+ resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- return GetBytes(chars, charCount, bytes, byteCount, null);
+ return GetBytesCommon(chars, charCount, bytes, byteCount);
}
public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
{
- fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
- fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
+ // It's ok for us to operate on null / empty spans.
+
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
{
- return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, baseEncoder: null);
+ return GetBytesCommon(charsPtr, chars.Length, bytesPtr, bytes.Length);
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe int GetBytesCommon(char* pChars, int charCount, byte* pBytes, int byteCount)
+ {
+ // Common helper method for all non-EncoderNLS entry points to GetBytes.
+ // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
+
+ Debug.Assert(charCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
+ Debug.Assert(byteCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
+
+ // First call into the fast path.
+
+ int bytesWritten = GetBytesFast(pChars, charCount, pBytes, byteCount, out int charsConsumed);
+
+ if (charsConsumed == charCount)
+ {
+ // All elements converted - return immediately.
+
+ return bytesWritten;
+ }
+ else
+ {
+ // Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
+
+ return GetBytesWithFallback(pChars, charCount, pBytes, byteCount, charsConsumed, bytesWritten);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetBytesCommon
+ private protected sealed override unsafe int GetBytesFast(char* pChars, int charsLength, byte* pBytes, int bytesLength, out int charsConsumed)
+ {
+ // We don't care about the exact OperationStatus value returned by the workhorse routine; we only
+ // care if the workhorse was able to consume the entire input payload. If we're unable to do so,
+ // we'll handle the remainder in the fallback routine.
+
+ Utf8Utility.TranscodeToUtf8(pChars, charsLength, pBytes, bytesLength, out char* pInputBufferRemaining, out byte* pOutputBufferRemaining);
+
+ charsConsumed = (int)(pInputBufferRemaining - pChars);
+ return (int)(pOutputBufferRemaining - pBytes);
+ }
+
// Returns the number of characters produced by decoding a range of bytes
// in a byte array.
//
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 is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
+ }
- if (bytes.Length - index < count)
- throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer);
+ if ((index | count) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException((index < 0) ? ExceptionArgument.index : ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- // If no input just return 0, fixed doesn't like 0 length arrays.
- if (count == 0)
- return 0;
+ if (bytes.Length - index < count)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
+ }
- // Just call pointer version
fixed (byte* pBytes = bytes)
- return GetCharCount(pBytes + index, count, null);
+ {
+ return GetCharCountCommon(pBytes + index, count);
+ }
}
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
public override unsafe int GetCharCount(byte* bytes, int count)
{
// Validate Parameters
+
if (bytes == null)
- throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array);
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
+ }
if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- return GetCharCount(bytes, count, null);
+ return GetCharCountCommon(bytes, count);
}
public override unsafe int GetCharCount(ReadOnlySpan<byte> bytes)
{
- fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
+ // It's ok for us to pass null pointers down to the workhorse routine.
+
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
{
- return GetCharCount(bytesPtr, bytes.Length, baseDecoder: null);
+ return GetCharCountCommon(bytesPtr, bytes.Length);
}
}
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 (bytes is null || chars is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(
+ argument: (bytes is null) ? ExceptionArgument.bytes : ExceptionArgument.chars,
+ resource: ExceptionResource.ArgumentNull_Array);
+ }
- if (charIndex < 0 || charIndex > chars.Length)
- throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index);
+ if ((byteIndex | byteCount) < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (byteIndex < 0) ? ExceptionArgument.byteIndex : ExceptionArgument.byteCount,
+ resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
- // If no input, return 0 & avoid fixed problem
- if (byteCount == 0)
- return 0;
+ if (bytes.Length - byteIndex < byteCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
+ }
- // Just call pointer version
- int charCount = chars.Length - charIndex;
+ if ((uint)charIndex > (uint)chars.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.charIndex, ExceptionResource.ArgumentOutOfRange_Index);
+ }
- 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);
+ fixed (byte* pBytes = bytes)
+ fixed (char* pChars = chars)
+ {
+ return GetCharsCommon(pBytes + byteIndex, byteCount, pChars + charIndex, chars.Length - charIndex);
+ }
}
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
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);
- }
+ if (bytes is null || chars is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(
+ argument: (bytes is null) ? ExceptionArgument.bytes : ExceptionArgument.chars,
+ resource: ExceptionResource.ArgumentNull_Array);
+ }
- public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
- {
- fixed (byte* bytesPtr = &MemoryMarshal.GetNonNullPinnableReference(bytes))
- fixed (char* charsPtr = &MemoryMarshal.GetNonNullPinnableReference(chars))
+ if ((byteCount | charCount) < 0)
{
- return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, baseDecoder: null);
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (byteCount < 0) ? ExceptionArgument.byteCount : ExceptionArgument.charCount,
+ resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
- }
- // 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
+ return GetCharsCommon(bytes, byteCount, chars, charCount);
+ }
- public override unsafe string GetString(byte[] bytes, int index, int count)
+ public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
{
- // 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;
+ // It's ok for us to pass null pointers down to the workhorse below.
- fixed (byte* pBytes = bytes)
- return string.CreateStringFromEncoding(
- pBytes + index, count, this);
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ {
+ return GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length);
+ }
}
+ // WARNING: If we throw an error, then System.Resources.ResourceReader calls this method.
+ // So if we're really broken, then that could also throw an error... recursively.
+ // So try to make sure GetChars can at least process all uses by
+ // System.Resources.ResourceReader!
//
- // End of standard methods copied from EncodingNLS.cs
- //
-
- // To simplify maintenance, the structure of GetByteCount and GetBytes should be
- // kept the same as much as possible
- internal sealed override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
+ // Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
+ // If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount)
{
- // For fallback we may need a fallback buffer.
- // We wait to initialize it though in case we don't have any broken input unicode
- EncoderFallbackBuffer fallbackBuffer = null;
- char* pSrcForFallback;
+ // Common helper method for all non-DecoderNLS entry points to GetChars.
+ // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
- char* pSrc = chars;
- char* pEnd = pSrc + count;
+ Debug.Assert(byteCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
+ Debug.Assert(charCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pChars != null || charCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
- // Start by assuming we have as many as count
- int byteCount = count;
+ // First call into the fast path.
- int ch = 0;
+ int charsWritten = GetCharsFast(pBytes, byteCount, pChars, charCount, out int bytesConsumed);
- if (baseEncoder != null)
+ if (bytesConsumed == byteCount)
{
- UTF8Encoder encoder = (UTF8Encoder)baseEncoder;
- ch = encoder.surrogateChar;
-
- // 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()));
+ // All elements converted - return immediately.
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(chars, pEnd, encoder, false);
- }
+ return charsWritten;
}
-
- for (;;)
+ else
{
- // SLOWLOOP: does all range checks, handles all special cases, but it is slow
- if (pSrc >= pEnd)
- {
- if (ch == 0)
- {
- // Unroll any fallback that happens at the end
- ch = fallbackBuffer != null ? fallbackBuffer.InternalGetNextChar() : 0;
- if (ch > 0)
- {
- byteCount++;
- goto ProcessChar;
- }
- }
- else
- {
- // Case of surrogates in the fallback.
- if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
- {
- Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
- "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
-
- ch = fallbackBuffer.InternalGetNextChar();
- byteCount++;
-
- if (InRange(ch, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- ch = 0xfffd;
- byteCount++;
- goto EncodeChar;
- }
- else if (ch > 0)
- {
- goto ProcessChar;
- }
- else
- {
- byteCount--; // ignore last one.
- break;
- }
- }
- }
-
- if (ch <= 0)
- {
- break;
- }
- if (baseEncoder != null && !baseEncoder.MustFlush)
- {
- break;
- }
-
- // attempt to encode the partial surrogate (will fallback or ignore it), it'll also subtract 1.
- byteCount++;
- goto EncodeChar;
- }
-
- if (ch > 0)
- {
- Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
- "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
-
- // use separate helper variables for local contexts so that the jit optimizations
- // won't get confused about the variable lifetimes
- int cha = *pSrc;
-
- // count the pending surrogate
- byteCount++;
-
- // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
- // if (IsLowSurrogate(cha)) {
- if (InRange(cha, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // Don't need a real # because we're just counting, anything > 0x7ff ('cept surrogate) will do.
- ch = 0xfffd;
- // ch = cha + (ch << 10) +
- // (0x10000
- // - CharUnicodeInfo.LOW_SURROGATE_START
- // - (CharUnicodeInfo.HIGH_SURROGATE_START << 10) );
-
- // Use this next char
- pSrc++;
- }
- // else ch is still high surrogate and encoding will fail (so don't add count)
-
- // attempt to encode the surrogate or partial surrogate
- goto EncodeChar;
- }
-
- // If we've used a fallback, then we have to check for it
- if (fallbackBuffer != null)
- {
- ch = fallbackBuffer.InternalGetNextChar();
- if (ch > 0)
- {
- // We have an extra byte we weren't expecting.
- byteCount++;
- goto ProcessChar;
- }
- }
-
- // read next char. The JIT optimization seems to be getting confused when
- // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
- ch = *pSrc;
- pSrc++;
-
- ProcessChar:
- // if (IsHighSurrogate(ch)) {
- if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.HIGH_SURROGATE_END))
- {
- // we will count this surrogate next time around
- byteCount--;
- continue;
- }
- // either good char or partial surrogate
-
- EncodeChar:
- // throw exception on partial surrogate if necessary
- // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
- if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // Lone surrogates aren't allowed
- // Have to make a fallback buffer if we don't have one
- if (fallbackBuffer == null)
- {
- // wait on fallbacks if we can
- // For fallback we may need a fallback buffer
- if (baseEncoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = baseEncoder.FallbackBuffer;
-
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(chars, chars + count, baseEncoder, false);
- }
-
- // Do our fallback. Actually we already know its a mixed up surrogate,
- // so the ref pSrc isn't gonna do anything.
- pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(unchecked((char)ch), ref pSrcForFallback);
- pSrc = pSrcForFallback;
-
- // Ignore it if we don't throw (we had preallocated this ch)
- byteCount--;
- ch = 0;
- continue;
- }
-
- // Count them
- if (ch > 0x7F)
- {
- if (ch > 0x7FF)
- {
- // the extra surrogate byte was compensated by the second surrogate character
- // (2 surrogates make 4 bytes. We've already counted 2 bytes, 1 per char)
- byteCount++;
- }
- byteCount++;
- }
-
-#if BIT64
- // check for overflow
- if (byteCount < 0)
- {
- break;
- }
-#endif
-
-#if FASTLOOP
- // If still have fallback don't do fast loop
- if (fallbackBuffer != null && (ch = fallbackBuffer.InternalGetNextChar()) != 0)
- {
- // We're reserving 1 byte for each char by default
- byteCount++;
- goto ProcessChar;
- }
-
- int availableChars = PtrDiff(pEnd, pSrc);
+ // Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
- // don't fall into the fast decoding loop if we don't have enough characters
- if (availableChars <= 13)
- {
- // try to get over the remainder of the ascii characters fast though
- char* pLocalEnd = pEnd; // hint to get pLocalEnd en-registered
- while (pSrc < pLocalEnd)
- {
- ch = *pSrc;
- pSrc++;
- if (ch > 0x7F)
- goto ProcessChar;
- }
-
- // we are done
- break;
- }
-
-#if BIT64
- // make sure that we won't get a silent overflow inside the fast loop
- // (Fall out to slow loop if we have this many characters)
- availableChars &= 0x0FFFFFFF;
-#endif
-
- // To compute the upper bound, assume that all characters are ASCII characters at this point,
- // the boundary will be decreased for every non-ASCII character we encounter
- // Also, we need 3 + 4 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
- char* pStop = pSrc + availableChars - (3 + 4);
-
- while (pSrc < pStop)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F) // Not ASCII
- {
- if (ch > 0x7FF) // Not 2 Byte
- {
- if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
- goto LongCode;
- byteCount++;
- }
- byteCount++;
- }
-
- // get pSrc aligned
- if ((unchecked((int)pSrc) & 0x2) != 0)
- {
- ch = *pSrc;
- pSrc++;
- if (ch > 0x7F) // Not ASCII
- {
- if (ch > 0x7FF) // Not 2 Byte
- {
- if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
- goto LongCode;
- byteCount++;
- }
- byteCount++;
- }
- }
-
- // Run 2 * 4 characters at a time!
- while (pSrc < pStop)
- {
- ch = *(int*)pSrc;
- int chc = *(int*)(pSrc + 2);
- if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0) // See if not ASCII
- {
- if (((ch | chc) & unchecked((int)0xF800F800)) != 0) // See if not 2 Byte
- {
- goto LongCodeWithMask;
- }
-
-
- if ((ch & unchecked((int)0xFF800000)) != 0) // Actually 0x07800780 is all we care about (4 bits)
- byteCount++;
- if ((ch & unchecked((int)0xFF80)) != 0)
- byteCount++;
- if ((chc & unchecked((int)0xFF800000)) != 0)
- byteCount++;
- if ((chc & unchecked((int)0xFF80)) != 0)
- byteCount++;
- }
- pSrc += 4;
-
- ch = *(int*)pSrc;
- chc = *(int*)(pSrc + 2);
- if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0) // See if not ASCII
- {
- if (((ch | chc) & unchecked((int)0xF800F800)) != 0) // See if not 2 Byte
- {
- goto LongCodeWithMask;
- }
-
- if ((ch & unchecked((int)0xFF800000)) != 0)
- byteCount++;
- if ((ch & unchecked((int)0xFF80)) != 0)
- byteCount++;
- if ((chc & unchecked((int)0xFF800000)) != 0)
- byteCount++;
- if ((chc & unchecked((int)0xFF80)) != 0)
- byteCount++;
- }
- pSrc += 4;
- }
- break;
-
- LongCodeWithMask:
- if (BitConverter.IsLittleEndian)
- {
- ch = (char)ch;
- }
- else
- {
- // be careful about the sign extension
- ch = (int)(((uint)ch) >> 16);
- }
- pSrc++;
-
- if (ch <= 0x7F)
- {
- continue;
- }
-
- LongCode:
- // use separate helper variables for slow and fast loop so that the jit optimizations
- // won't get confused about the variable lifetimes
- if (ch > 0x7FF)
- {
- // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
- if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // 4 byte encoding - high surrogate + low surrogate
-
- int chd = *pSrc;
- if (
- // !IsHighSurrogate(ch) // low without high -> bad
- ch > CharUnicodeInfo.HIGH_SURROGATE_END ||
- // !IsLowSurrogate(chd) // high not followed by low -> bad
- !InRange(chd, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // Back up and drop out to slow loop to figure out error
- pSrc--;
- break;
- }
- pSrc++;
-
- // byteCount - this byte is compensated by the second surrogate character
- }
- byteCount++;
- }
- byteCount++;
-
- // byteCount - the last byte is already included
- }
-#endif // FASTLOOP
-
- // no pending char at this point
- ch = 0;
+ return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten);
}
-
-#if BIT64
- // check for overflow
- if (byteCount < 0)
- {
- throw new ArgumentException(
- SR.Argument_ConversionOverflow);
- }
-#endif
-
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
- "[UTF8Encoding.GetByteCount]Expected Empty fallback buffer");
-
- return byteCount;
}
- // diffs two char pointers using unsigned arithmetic. The unsigned arithmetic
- // is good enough for us, and it tends to generate better code than the signed
- // arithmetic generated by default
- private static unsafe int PtrDiff(char* a, char* b)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetCharsCommon
+ private protected sealed override unsafe int GetCharsFast(byte* pBytes, int bytesLength, char* pChars, int charsLength, out int bytesConsumed)
{
- return (int)(((uint)((byte*)a - (byte*)b)) >> 1);
- }
+ // We don't care about the exact OperationStatus value returned by the workhorse routine; we only
+ // care if the workhorse was able to consume the entire input payload. If we're unable to do so,
+ // we'll handle the remainder in the fallback routine.
- // byte* flavor just for parity
- private static unsafe int PtrDiff(byte* a, byte* b)
- {
- return (int)(a - b);
- }
+ Utf8Utility.TranscodeToUtf16(pBytes, bytesLength, pChars, charsLength, out byte* pInputBufferRemaining, out char* pOutputBufferRemaining);
- private static bool InRange(int ch, int start, int end)
- {
- return (uint)(ch - start) <= (uint)(end - start);
+ bytesConsumed = (int)(pInputBufferRemaining - pBytes);
+ return (int)(pOutputBufferRemaining - pChars);
}
- // Our workhorse
- // Note: We ignore mismatched surrogates, unless the exception flag is set in which case we throw
- internal sealed override unsafe int GetBytes(
- char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder)
+ private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS decoder)
{
- Debug.Assert(chars != null, "[UTF8Encoding.GetBytes]chars!=null");
- Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetBytes]byteCount >=0");
- Debug.Assert(charCount >= 0, "[UTF8Encoding.GetBytes]charCount >=0");
- Debug.Assert(bytes != null, "[UTF8Encoding.GetBytes]bytes!=null");
-
- UTF8Encoder encoder = null;
-
- // For fallback we may need a fallback buffer.
- // We wait to initialize it though in case we don't have any broken input unicode
- EncoderFallbackBuffer fallbackBuffer = null;
- char* pSrcForFallback;
-
- char* pSrc = chars;
- byte* pTarget = bytes;
-
- char* pEnd = pSrc + charCount;
- byte* pAllocatedBufferEnd = pTarget + byteCount;
-
- int ch = 0;
-
- // assume that JIT will en-register pSrc, pTarget and ch
+ // We special-case DecoderReplacementFallback if it's telling us to write a single U+FFFD char,
+ // since we believe this to be relatively common and we can handle it more efficiently than
+ // the base implementation.
- if (baseEncoder != null)
+ if (((decoder is null) ? this.DecoderFallback : decoder.Fallback) is DecoderReplacementFallback replacementFallback
+ && replacementFallback.MaxCharCount == 1
+ && replacementFallback.DefaultString[0] == UnicodeUtility.ReplacementChar)
{
- encoder = (UTF8Encoder)baseEncoder;
- ch = encoder.surrogateChar;
-
- // 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(chars, pEnd, encoder, true);
- }
- }
-
- for (;;)
- {
- // SLOWLOOP: does all range checks, handles all special cases, but it is slow
-
- if (pSrc >= pEnd)
- {
- if (ch == 0)
- {
- // Check if there's anything left to get out of the fallback buffer
- ch = fallbackBuffer != null ? fallbackBuffer.InternalGetNextChar() : 0;
- if (ch > 0)
- {
- goto ProcessChar;
- }
- }
- else
- {
- // Case of leftover surrogates in the fallback buffer
- if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
- {
- Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
- "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
-
- int cha = ch;
-
- ch = fallbackBuffer.InternalGetNextChar();
-
- if (InRange(ch, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- ch = ch + (cha << 10) + (0x10000 - CharUnicodeInfo.LOW_SURROGATE_START - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
- goto EncodeChar;
- }
- else if (ch > 0)
- {
- goto ProcessChar;
- }
- else
- {
- break;
- }
- }
- }
-
- // attempt to encode the partial surrogate (will fail or ignore)
- if (ch > 0 && (encoder == null || encoder.MustFlush))
- goto EncodeChar;
-
- // We're done
- break;
- }
-
- if (ch > 0)
- {
- // We have a high surrogate left over from a previous loop.
- Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
- "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
-
- // use separate helper variables for local contexts so that the jit optimizations
- // won't get confused about the variable lifetimes
- int cha = *pSrc;
-
- // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
- // if (IsLowSurrogate(cha)) {
- if (InRange(cha, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- ch = cha + (ch << 10) +
- (0x10000
- - CharUnicodeInfo.LOW_SURROGATE_START
- - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
-
- pSrc++;
- }
- // else ch is still high surrogate and encoding will fail
-
- // attempt to encode the surrogate or partial surrogate
- goto EncodeChar;
- }
-
- // If we've used a fallback, then we have to check for it
- if (fallbackBuffer != null)
- {
- ch = fallbackBuffer.InternalGetNextChar();
- if (ch > 0) goto ProcessChar;
- }
-
- // read next char. The JIT optimization seems to be getting confused when
- // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
- ch = *pSrc;
- pSrc++;
-
- ProcessChar:
- // if (IsHighSurrogate(ch)) {
- if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.HIGH_SURROGATE_END))
- {
- continue;
- }
- // either good char or partial surrogate
-
- EncodeChar:
- // throw exception on partial surrogate if necessary
- // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
- if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // Lone surrogates aren't allowed, we have to do fallback for them
- // Have to make a fallback buffer if we don't have one
- if (fallbackBuffer == null)
- {
- // wait on fallbacks if we can
- // For fallback we may need a fallback buffer
- if (baseEncoder == null)
- fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
- else
- fallbackBuffer = baseEncoder.FallbackBuffer;
-
- // Set our internal fallback interesting things.
- fallbackBuffer.InternalInitialize(chars, pEnd, baseEncoder, true);
- }
-
- // Do our fallback. Actually we already know its a mixed up surrogate,
- // so the ref pSrc isn't gonna do anything.
- pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be en-registered
- fallbackBuffer.InternalFallback(unchecked((char)ch), ref pSrcForFallback);
- pSrc = pSrcForFallback;
-
- // Ignore it if we don't throw
- ch = 0;
- continue;
- }
-
- // Count bytes needed
- int bytesNeeded = 1;
- if (ch > 0x7F)
- {
- if (ch > 0x7FF)
- {
- if (ch > 0xFFFF)
- {
- bytesNeeded++; // 4 bytes (surrogate pair)
- }
- bytesNeeded++; // 3 bytes (800-FFFF)
- }
- bytesNeeded++; // 2 bytes (80-7FF)
- }
-
- if (pTarget > pAllocatedBufferEnd - bytesNeeded)
- {
- // Left over surrogate from last time will cause pSrc == chars, so we'll throw
- if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
- {
- fallbackBuffer.MovePrevious(); // Didn't use this fallback char
- if (ch > 0xFFFF)
- fallbackBuffer.MovePrevious(); // Was surrogate, didn't use 2nd part either
- }
- else
- {
- pSrc--; // Didn't use this char
- if (ch > 0xFFFF)
- pSrc--; // Was surrogate, didn't use 2nd part either
- }
- Debug.Assert(pSrc >= chars || pTarget == bytes,
- "[UTF8Encoding.GetBytes]Expected pSrc to be within buffer or to throw with insufficient room.");
- ThrowBytesOverflow(encoder, pTarget == bytes); // Throw if we must
- ch = 0; // Nothing left over (we backed up to start of pair if supplementary)
- break;
- }
-
- if (ch <= 0x7F)
- {
- *pTarget = (byte)ch;
- }
- else
- {
- // use separate helper variables for local contexts so that the jit optimizations
- // won't get confused about the variable lifetimes
- int chb;
- if (ch <= 0x7FF)
- {
- // 2 byte encoding
- chb = (byte)(unchecked((sbyte)0xC0) | (ch >> 6));
- }
- else
- {
- if (ch <= 0xFFFF)
- {
- chb = (byte)(unchecked((sbyte)0xE0) | (ch >> 12));
- }
- else
- {
- *pTarget = (byte)(unchecked((sbyte)0xF0) | (ch >> 18));
- pTarget++;
-
- chb = unchecked((sbyte)0x80) | (ch >> 12) & 0x3F;
- }
- *pTarget = (byte)chb;
- pTarget++;
-
- chb = unchecked((sbyte)0x80) | (ch >> 6) & 0x3F;
- }
- *pTarget = (byte)chb;
- pTarget++;
-
- *pTarget = (byte)(unchecked((sbyte)0x80) | ch & 0x3F);
- }
- pTarget++;
-
-
-#if FASTLOOP
- // If still have fallback don't do fast loop
- if (fallbackBuffer != null && (ch = fallbackBuffer.InternalGetNextChar()) != 0)
- goto ProcessChar;
-
- int availableChars = PtrDiff(pEnd, pSrc);
- int availableBytes = PtrDiff(pAllocatedBufferEnd, pTarget);
-
- // don't fall into the fast decoding loop if we don't have enough characters
- // Note that if we don't have enough bytes, pStop will prevent us from entering the fast loop.
- if (availableChars <= 13)
- {
- // we are hoping for 1 byte per char
- if (availableBytes < availableChars)
- {
- // not enough output room. no pending bits at this point
- ch = 0;
- continue;
- }
-
- // try to get over the remainder of the ascii characters fast though
- char* pLocalEnd = pEnd; // hint to get pLocalEnd en-registered
- while (pSrc < pLocalEnd)
- {
- ch = *pSrc;
- pSrc++;
-
- // Not ASCII, need more than 1 byte per char
- if (ch > 0x7F)
- goto ProcessChar;
-
- *pTarget = (byte)ch;
- pTarget++;
- }
- // we are done, let ch be 0 to clear encoder
- ch = 0;
- break;
- }
-
- // we need at least 1 byte per character, but Convert might allow us to convert
- // only part of the input, so try as much as we can. Reduce charCount if necessary
- if (availableBytes < availableChars)
- {
- availableChars = availableBytes;
- }
-
- // FASTLOOP:
- // - optimistic range checks
- // - fallbacks to the slow loop for all special cases, exception throwing, etc.
+ // Don't care about the exact OperationStatus, just how much of the payload we were able
+ // to process.
- // To compute the upper bound, assume that all characters are ASCII characters at this point,
- // the boundary will be decreased for every non-ASCII character we encounter
- // Also, we need 5 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
- // If there aren't enough bytes for the output, then pStop will be <= pSrc and will bypass the loop.
- char* pStop = pSrc + availableChars - 5;
+ Utf8.ToUtf16(bytes, chars, out int bytesRead, out int charsWritten, replaceInvalidSequences: true, isFinalBlock: decoder is null || decoder.MustFlush);
- while (pSrc < pStop)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- *pTarget = (byte)ch;
- pTarget++;
-
- // get pSrc aligned
- if ((unchecked((int)pSrc) & 0x2) != 0)
- {
- ch = *pSrc;
- pSrc++;
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- *pTarget = (byte)ch;
- pTarget++;
- }
-
- // Run 4 characters at a time!
- while (pSrc < pStop)
- {
- ch = *(int*)pSrc;
- int chc = *(int*)(pSrc + 2);
- if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0)
- {
- goto LongCodeWithMask;
- }
-
- // Unfortunately, this is endianess sensitive
- if (BitConverter.IsLittleEndian)
- {
- *pTarget = (byte)ch;
- *(pTarget + 1) = (byte)(ch >> 16);
- pSrc += 4;
- *(pTarget + 2) = (byte)chc;
- *(pTarget + 3) = (byte)(chc >> 16);
- pTarget += 4;
- }
- else
- {
- *pTarget = (byte)(ch>>16);
- *(pTarget+1) = (byte)ch;
- pSrc += 4;
- *(pTarget+2) = (byte)(chc>>16);
- *(pTarget+3) = (byte)chc;
- pTarget += 4;
- }
- }
- continue;
-
- LongCodeWithMask:
- if (BitConverter.IsLittleEndian)
- {
- ch = (char)ch;
- }
- else
- {
- // be careful about the sign extension
- ch = (int)(((uint)ch) >> 16);
- }
- pSrc++;
-
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- *pTarget = (byte)ch;
- pTarget++;
- continue;
-
- LongCode:
- // use separate helper variables for slow and fast loop so that the jit optimizations
- // won't get confused about the variable lifetimes
- int chd;
- if (ch <= 0x7FF)
- {
- // 2 byte encoding
- chd = unchecked((sbyte)0xC0) | (ch >> 6);
- }
- else
- {
- // if (!IsLowSurrogate(ch) && !IsHighSurrogate(ch))
- if (!InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // 3 byte encoding
- chd = unchecked((sbyte)0xE0) | (ch >> 12);
- }
- else
- {
- // 4 byte encoding - high surrogate + low surrogate
- // if (!IsHighSurrogate(ch))
- if (ch > CharUnicodeInfo.HIGH_SURROGATE_END)
- {
- // low without high -> bad, try again in slow loop
- pSrc -= 1;
- break;
- }
-
- chd = *pSrc;
- pSrc++;
-
- // if (!IsLowSurrogate(chd)) {
- if (!InRange(chd, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
- {
- // high not followed by low -> bad, try again in slow loop
- pSrc -= 2;
- break;
- }
-
- ch = chd + (ch << 10) +
- (0x10000
- - CharUnicodeInfo.LOW_SURROGATE_START
- - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
-
- *pTarget = (byte)(unchecked((sbyte)0xF0) | (ch >> 18));
- // pStop - this byte is compensated by the second surrogate character
- // 2 input chars require 4 output bytes. 2 have been anticipated already
- // and 2 more will be accounted for by the 2 pStop-- calls below.
- pTarget++;
-
- chd = unchecked((sbyte)0x80) | (ch >> 12) & 0x3F;
- }
- *pTarget = (byte)chd;
- pStop--; // 3 byte sequence for 1 char, so need pStop-- and the one below too.
- pTarget++;
-
- chd = unchecked((sbyte)0x80) | (ch >> 6) & 0x3F;
- }
- *pTarget = (byte)chd;
- pStop--; // 2 byte sequence for 1 char so need pStop--.
- pTarget++;
-
- *pTarget = (byte)(unchecked((sbyte)0x80) | ch & 0x3F);
- // pStop - this byte is already included
- pTarget++;
- }
+ // Slice off how much we consumed / wrote.
- Debug.Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetBytes]pTarget <= pAllocatedBufferEnd");
+ bytes = bytes.Slice(bytesRead);
+ chars = chars.Slice(charsWritten);
+ }
-#endif // FASTLOOP
+ // If we couldn't go through our fast fallback mechanism, or if we still have leftover
+ // data because we couldn't consume everything in the loop above, we need to go down the
+ // slow fallback path.
- // no pending char at this point
- ch = 0;
+ if (bytes.IsEmpty)
+ {
+ return originalCharsLength - chars.Length; // total number of chars written
}
-
- // Do we have to set the encoder bytes?
- if (encoder != null)
+ else
{
- Debug.Assert(!encoder.MustFlush || ch == 0,
- "[UTF8Encoding.GetBytes] Expected no mustflush or 0 leftover ch " + ch.ToString("X2", CultureInfo.InvariantCulture));
-
- encoder.surrogateChar = ch;
- encoder._charsUsed = (int)(pSrc - chars);
+ return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
}
-
- Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
- baseEncoder == null || !baseEncoder._throwOnOverflow,
- "[UTF8Encoding.GetBytes]Expected empty fallback buffer if not converting");
-
- return (int)(pTarget - bytes);
}
-
- // These are bitmasks used to maintain the state in the decoder. They occupy the higher bits
- // while the actual character is being built in the lower bits. They are shifted together
- // with the actual bits of the character.
-
- // bits 30 & 31 are used for pending bits fixup
- private const int FinalByte = 1 << 29;
- private const int SupplimentarySeq = 1 << 28;
- private const int ThreeByteSeq = 1 << 27;
-
- // Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
- // If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
//
- // To simplify maintenance, the structure of GetCharCount and GetChars should be
- // kept the same as much as possible
- internal sealed override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
- {
- Debug.Assert(count >= 0, "[UTF8Encoding.GetCharCount]count >=0");
- Debug.Assert(bytes != null, "[UTF8Encoding.GetCharCount]bytes!=null");
-
- // Initialize stuff
- byte* pSrc = bytes;
- byte* pEnd = pSrc + count;
+ // 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
- // Start by assuming we have as many as count, charCount always includes the adjustment
- // for the character being decoded
- int charCount = count;
- int ch = 0;
- DecoderFallbackBuffer fallback = null;
+ public override unsafe string GetString(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
- if (baseDecoder != null)
+ if (bytes is null)
{
- UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
- ch = decoder.bits;
- charCount -= (ch >> 30); // Adjust char count for # of expected bytes and expected output chars.
-
- // 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,
- "[UTF8Encoding.GetCharCount]Expected empty fallback buffer at start");
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.bytes, ExceptionResource.ArgumentNull_Array);
}
- for (;;)
+ if ((index | count) < 0)
{
- // SLOWLOOP: does all range checks, handles all special cases, but it is slow
-
- if (pSrc >= pEnd)
- {
- break;
- }
-
- if (ch == 0)
- {
- // no pending bits
- goto ReadChar;
- }
-
- // read next byte. The JIT optimization seems to be getting confused when
- // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
- int cha = *pSrc;
- pSrc++;
-
- // we are expecting to see trailing bytes like 10vvvvvv
- if ((cha & unchecked((sbyte)0xC0)) != 0x80)
- {
- // This can be a valid starting byte for another UTF8 byte sequence, so let's put
- // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
- pSrc--;
- charCount += (ch >> 30);
- goto InvalidByteSequence;
- }
-
- // fold in the new byte
- ch = (ch << 6) | (cha & 0x3F);
-
- if ((ch & FinalByte) == 0)
- {
- Debug.Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
- "[UTF8Encoding.GetChars]Invariant volation");
-
- if ((ch & SupplimentarySeq) != 0)
- {
- if ((ch & (FinalByte >> 6)) != 0)
- {
- // this is 3rd byte (of 4 byte supplementary) - nothing to do
- continue;
- }
-
- // 2nd byte, check for non-shortest form of supplementary char and the valid
- // supplementary characters in range 0x010000 - 0x10FFFF at the same time
- if (!InRange(ch & 0x1F0, 0x10, 0x100))
- {
- goto InvalidByteSequence;
- }
- }
- else
- {
- // Must be 2nd byte of a 3-byte sequence
- // check for non-shortest form of 3 byte seq
- if ((ch & (0x1F << 5)) == 0 || // non-shortest form
- (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
- {
- goto InvalidByteSequence;
- }
- }
- continue;
- }
-
- // ready to punch
-
- // adjust for surrogates in non-shortest form
- if ((ch & (SupplimentarySeq | 0x1F0000)) == SupplimentarySeq)
- {
- charCount--;
- }
- goto EncodeChar;
-
- InvalidByteSequence:
- // this code fragment should be close to the goto referencing it
- // Have to do fallback for invalid bytes
- if (fallback == null)
- {
- if (baseDecoder == null)
- fallback = this.decoderFallback.CreateFallbackBuffer();
- else
- fallback = baseDecoder.FallbackBuffer;
- fallback.InternalInitialize(bytes, null);
- }
- charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
-
- ch = 0;
- continue;
-
- ReadChar:
- ch = *pSrc;
- pSrc++;
-
- ProcessChar:
- if (ch > 0x7F)
- {
- // If its > 0x7F, its start of a new multi-byte sequence
-
- // Long sequence, so unreserve our char.
- charCount--;
-
- // bit 6 has to be non-zero for start of multibyte chars.
- if ((ch & 0x40) == 0)
- {
- // Unexpected trail byte
- goto InvalidByteSequence;
- }
-
- // start a new long code
- if ((ch & 0x20) != 0)
- {
- if ((ch & 0x10) != 0)
- {
- // 4 byte encoding - supplimentary character (2 surrogates)
-
- ch &= 0x0F;
-
- // check that bit 4 is zero and the valid supplimentary character
- // range 0x000000 - 0x10FFFF at the same time
- if (ch > 0x04)
- {
- ch |= 0xf0;
- goto InvalidByteSequence;
- }
-
- // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
- // Final byte flag, count fix if we don't make final byte & supplimentary sequence flag.
- ch |= (FinalByte >> 3 * 6) | // Final byte is 3 more bytes from now
- (1 << 30) | // If it dies on next byte we'll need an extra char
- (3 << (30 - 2 * 6)) | // If it dies on last byte we'll need to subtract a char
- (SupplimentarySeq) | (SupplimentarySeq >> 6) |
- (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
-
- // Our character count will be 2 characters for these 4 bytes, so subtract another char
- charCount--;
- }
- else
- {
- // 3 byte encoding
- // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
- ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
- (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
-
- // We'll expect 1 character for these 3 bytes, so subtract another char.
- charCount--;
- }
- }
- else
- {
- // 2 byte encoding
-
- ch &= 0x1F;
-
- // check for non-shortest form
- if (ch <= 1)
- {
- ch |= 0xc0;
- goto InvalidByteSequence;
- }
-
- // Add bit flags so we'll be flagged correctly
- ch |= (FinalByte >> 6);
- }
- continue;
- }
-
- EncodeChar:
-
-#if FASTLOOP
- int availableBytes = PtrDiff(pEnd, pSrc);
-
- // don't fall into the fast decoding loop if we don't have enough bytes
- if (availableBytes <= 13)
- {
- // try to get over the remainder of the ascii characters fast though
- byte* pLocalEnd = pEnd; // hint to get pLocalEnd en-registered
- while (pSrc < pLocalEnd)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F)
- goto ProcessChar;
- }
- // we are done
- ch = 0;
- break;
- }
-
- // To compute the upper bound, assume that all characters are ASCII characters at this point,
- // the boundary will be decreased for every non-ASCII character we encounter
- // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
- byte* pStop = pSrc + availableBytes - 7;
-
- while (pSrc < pStop)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F)
- {
- goto LongCode;
- }
-
- // get pSrc 2-byte aligned
- if ((unchecked((int)pSrc) & 0x1) != 0)
- {
- ch = *pSrc;
- pSrc++;
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- }
-
- // get pSrc 4-byte aligned
- if ((unchecked((int)pSrc) & 0x2) != 0)
- {
- ch = *(ushort*)pSrc;
- if ((ch & 0x8080) != 0)
- {
- goto LongCodeWithMask16;
- }
- pSrc += 2;
- }
-
- // Run 8 + 8 characters at a time!
- while (pSrc < pStop)
- {
- ch = *(int*)pSrc;
- int chb = *(int*)(pSrc + 4);
- if (((ch | chb) & unchecked((int)0x80808080)) != 0)
- {
- goto LongCodeWithMask32;
- }
- pSrc += 8;
-
- // This is a really small loop - unroll it
- if (pSrc >= pStop)
- break;
-
- ch = *(int*)pSrc;
- chb = *(int*)(pSrc + 4);
- if (((ch | chb) & unchecked((int)0x80808080)) != 0)
- {
- goto LongCodeWithMask32;
- }
- pSrc += 8;
- }
- break;
-
- LongCodeWithMask32:
- if (BitConverter.IsLittleEndian)
- {
- ch &= 0xFF;
- }
- else
- {
- // be careful about the sign extension
- ch = (int)(((uint)ch) >> 16);
- }
- LongCodeWithMask16:
- if (BitConverter.IsLittleEndian)
- {
- ch &= 0xFF;
- }
- else
- {
- ch = (int)(((uint)ch) >> 8);
- }
-
- pSrc++;
- if (ch <= 0x7F)
- {
- continue;
- }
-
- LongCode:
- int chc = *pSrc;
- pSrc++;
-
- if (
- // bit 6 has to be zero
- (ch & 0x40) == 0 ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (chc & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
-
- chc &= 0x3F;
-
- // start a new long code
- if ((ch & 0x20) != 0)
- {
- // fold the first two bytes together
- chc |= (ch & 0x0F) << 6;
-
- if ((ch & 0x10) != 0)
- {
- // 4 byte encoding - surrogate
- ch = *pSrc;
- if (
- // check that bit 4 is zero, the non-shortest form of surrogate
- // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
- !InRange(chc >> 4, 0x01, 0x10) ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
-
- chc = (chc << 6) | (ch & 0x3F);
-
- ch = *(pSrc + 1);
- // we are expecting to see trailing bytes like 10vvvvvv
- if ((ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
- pSrc += 2;
-
- // extra byte
- charCount--;
- }
- else
- {
- // 3 byte encoding
- ch = *pSrc;
- if (
- // check for non-shortest form of 3 byte seq
- (chc & (0x1F << 5)) == 0 ||
- // Can't have surrogates here.
- (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
- pSrc++;
-
- // extra byte
- charCount--;
- }
- }
- else
- {
- // 2 byte encoding
-
- // check for non-shortest form
- if ((ch & 0x1E) == 0)
- {
- goto BadLongCode;
- }
- }
-
- // extra byte
- charCount--;
- }
-#endif // FASTLOOP
-
- // no pending bits at this point
- ch = 0;
- continue;
-
- BadLongCode:
- pSrc -= 2;
- ch = 0;
- continue;
+ ThrowHelper.ThrowArgumentOutOfRangeException(
+ argument: (index < 0) ? ExceptionArgument.index : ExceptionArgument.count,
+ resource: ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
- // May have a problem if we have to flush
- if (ch != 0)
+ if (bytes.Length - index < count)
{
- // We were already adjusting for these, so need to un-adjust
- charCount += (ch >> 30);
- if (baseDecoder == null || baseDecoder.MustFlush)
- {
- // Have to do fallback for invalid bytes
- if (fallback == null)
- {
- if (baseDecoder == null)
- fallback = this.decoderFallback.CreateFallbackBuffer();
- else
- fallback = baseDecoder.FallbackBuffer;
- fallback.InternalInitialize(bytes, null);
- }
- charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
- }
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.bytes, ExceptionResource.ArgumentOutOfRange_IndexCountBuffer);
}
- // Shouldn't have anything in fallback buffer for GetCharCount
- // (don't have to check _throwOnOverflow for count)
- Debug.Assert(fallback == null || fallback.Remaining == 0,
- "[UTF8Encoding.GetCharCount]Expected empty fallback buffer at end");
+ // Avoid problems with empty input buffer
+ if (count == 0)
+ return string.Empty;
- return charCount;
+ fixed (byte* pBytes = bytes)
+ {
+ return string.CreateStringFromEncoding(pBytes + index, count, this);
+ }
}
- // WARNING: If we throw an error, then System.Resources.ResourceReader calls this method.
- // So if we're really broken, then that could also throw an error... recursively.
- // So try to make sure GetChars can at least process all uses by
- // System.Resources.ResourceReader!
//
- // Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
- // If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
+ // End of standard methods copied from EncodingNLS.cs
//
- // To simplify maintenance, the structure of GetCharCount and GetChars should be
- // kept the same as much as possible
- internal sealed override unsafe int GetChars(
- byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder)
- {
- Debug.Assert(chars != null, "[UTF8Encoding.GetChars]chars!=null");
- Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetChars]count >=0");
- Debug.Assert(charCount >= 0, "[UTF8Encoding.GetChars]charCount >=0");
- Debug.Assert(bytes != null, "[UTF8Encoding.GetChars]bytes!=null");
-
- byte* pSrc = bytes;
- char* pTarget = chars;
-
- byte* pEnd = pSrc + byteCount;
- char* pAllocatedBufferEnd = pTarget + charCount;
-
- int ch = 0;
-
- DecoderFallbackBuffer fallback = null;
- byte* pSrcForFallback;
- char* pTargetForFallback;
- if (baseDecoder != null)
- {
- UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
- ch = decoder.bits;
-
- // Shouldn't have anything in fallback buffer for GetChars
- // (don't have to check _throwOnOverflow for chars, we always use all or none so always should be empty)
- Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
- "[UTF8Encoding.GetChars]Expected empty fallback buffer at start");
- }
- for (;;)
- {
- // SLOWLOOP: does all range checks, handles all special cases, but it is slow
-
- if (pSrc >= pEnd)
- {
- break;
- }
-
- if (ch == 0)
- {
- // no pending bits
- goto ReadChar;
- }
-
- // read next byte. The JIT optimization seems to be getting confused when
- // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
- int cha = *pSrc;
- pSrc++;
-
- // we are expecting to see trailing bytes like 10vvvvvv
- if ((cha & unchecked((sbyte)0xC0)) != 0x80)
- {
- // This can be a valid starting byte for another UTF8 byte sequence, so let's put
- // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
- pSrc--;
- goto InvalidByteSequence;
- }
-
- // fold in the new byte
- ch = (ch << 6) | (cha & 0x3F);
-
- if ((ch & FinalByte) == 0)
- {
- // Not at last byte yet
- Debug.Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
- "[UTF8Encoding.GetChars]Invariant volation");
-
- if ((ch & SupplimentarySeq) != 0)
- {
- // Its a 4-byte supplimentary sequence
- if ((ch & (FinalByte >> 6)) != 0)
- {
- // this is 3rd byte of 4 byte sequence - nothing to do
- continue;
- }
-
- // 2nd byte of 4 bytes
- // check for non-shortest form of surrogate and the valid surrogate
- // range 0x000000 - 0x10FFFF at the same time
- if (!InRange(ch & 0x1F0, 0x10, 0x100))
- {
- goto InvalidByteSequence;
- }
- }
- else
- {
- // Must be 2nd byte of a 3-byte sequence
- // check for non-shortest form of 3 byte seq
- if ((ch & (0x1F << 5)) == 0 || // non-shortest form
- (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
- {
- goto InvalidByteSequence;
- }
- }
- continue;
- }
-
- // ready to punch
-
- // surrogate in shortest form?
- // Might be possible to get rid of this? Already did non-shortest check for 4-byte sequence when reading 2nd byte?
- if ((ch & (SupplimentarySeq | 0x1F0000)) > SupplimentarySeq)
- {
- // let the range check for the second char throw the exception
- if (pTarget < pAllocatedBufferEnd)
- {
- *pTarget = (char)(((ch >> 10) & 0x7FF) +
- unchecked((short)((CharUnicodeInfo.HIGH_SURROGATE_START - (0x10000 >> 10)))));
- pTarget++;
-
- ch = (ch & 0x3FF) +
- unchecked((int)(CharUnicodeInfo.LOW_SURROGATE_START));
- }
- }
-
- goto EncodeChar;
-
- InvalidByteSequence:
- // this code fragment should be close to the gotos referencing it
- // Have to do fallback for invalid bytes
- if (fallback == null)
- {
- if (baseDecoder == null)
- fallback = this.decoderFallback.CreateFallbackBuffer();
- else
- fallback = baseDecoder.FallbackBuffer;
- fallback.InternalInitialize(bytes, pAllocatedBufferEnd);
- }
- // That'll back us up the appropriate # of bytes if we didn't get anywhere
- pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be en-registered
- pTargetForFallback = pTarget; // Avoid passing pTarget by reference to allow it to be en-registered
- bool fallbackResult = FallbackInvalidByteSequence(ref pSrcForFallback, ch, fallback, ref pTargetForFallback);
- pSrc = pSrcForFallback;
- pTarget = pTargetForFallback;
-
- if (!fallbackResult)
- {
- // Ran out of buffer space
- // Need to throw an exception?
- Debug.Assert(pSrc >= bytes || pTarget == chars,
- "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer after fallback");
- fallback.InternalReset();
- ThrowCharsOverflow(baseDecoder, pTarget == chars);
- ch = 0;
- break;
- }
- Debug.Assert(pSrc >= bytes,
- "[UTF8Encoding.GetChars]Expected invalid byte sequence to have remained within the byte array");
- ch = 0;
- continue;
-
- ReadChar:
- ch = *pSrc;
- pSrc++;
-
- ProcessChar:
- if (ch > 0x7F)
- {
- // If its > 0x7F, its start of a new multi-byte sequence
-
- // bit 6 has to be non-zero
- if ((ch & 0x40) == 0)
- {
- goto InvalidByteSequence;
- }
-
- // start a new long code
- if ((ch & 0x20) != 0)
- {
- if ((ch & 0x10) != 0)
- {
- // 4 byte encoding - supplimentary character (2 surrogates)
-
- ch &= 0x0F;
-
- // check that bit 4 is zero and the valid supplimentary character
- // range 0x000000 - 0x10FFFF at the same time
- if (ch > 0x04)
- {
- ch |= 0xf0;
- goto InvalidByteSequence;
- }
-
- ch |= (FinalByte >> 3 * 6) | (1 << 30) | (3 << (30 - 2 * 6)) |
- (SupplimentarySeq) | (SupplimentarySeq >> 6) |
- (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
- }
- else
- {
- // 3 byte encoding
- ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
- (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
- }
- }
- else
- {
- // 2 byte encoding
-
- ch &= 0x1F;
-
- // check for non-shortest form
- if (ch <= 1)
- {
- ch |= 0xc0;
- goto InvalidByteSequence;
- }
-
- ch |= (FinalByte >> 6);
- }
- continue;
- }
-
- EncodeChar:
- // write the pending character
- if (pTarget >= pAllocatedBufferEnd)
- {
- // Fix chars so we make sure to throw if we didn't output anything
- ch &= 0x1fffff;
- if (ch > 0x7f)
- {
- if (ch > 0x7ff)
- {
- if (ch >= CharUnicodeInfo.LOW_SURROGATE_START &&
- ch <= CharUnicodeInfo.LOW_SURROGATE_END)
- {
- pSrc--; // It was 4 bytes
- pTarget--; // 1 was stored already, but we can't remember 1/2, so back up
- }
- else if (ch > 0xffff)
- {
- pSrc--; // It was 4 bytes, nothing was stored
- }
- pSrc--; // It was at least 3 bytes
- }
- pSrc--; // It was at least 2 bytes
- }
- pSrc--;
-
- // Throw that we don't have enough room (pSrc could be < chars if we had started to process
- // a 4 byte sequence already)
- Debug.Assert(pSrc >= bytes || pTarget == chars,
- "[UTF8Encoding.GetChars]Expected pSrc to be within input buffer or throw due to no output]");
- ThrowCharsOverflow(baseDecoder, pTarget == chars);
-
- // Don't store ch in decoder, we already backed up to its start
- ch = 0;
-
- // Didn't throw, just use this buffer size.
- break;
- }
- *pTarget = (char)ch;
- pTarget++;
-
-#if FASTLOOP
- int availableChars = PtrDiff(pAllocatedBufferEnd, pTarget);
- int availableBytes = PtrDiff(pEnd, pSrc);
-
- // don't fall into the fast decoding loop if we don't have enough bytes
- // Test for availableChars is done because pStop would be <= pTarget.
- if (availableBytes <= 13)
- {
- // we may need as many as 1 character per byte
- if (availableChars < availableBytes)
- {
- // not enough output room. no pending bits at this point
- ch = 0;
- continue;
- }
-
- // try to get over the remainder of the ascii characters fast though
- byte* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
- while (pSrc < pLocalEnd)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F)
- goto ProcessChar;
-
- *pTarget = (char)ch;
- pTarget++;
- }
- // we are done
- ch = 0;
- break;
- }
-
- // we may need as many as 1 character per byte, so reduce the byte count if necessary.
- // If availableChars is too small, pStop will be before pTarget and we won't do fast loop.
- if (availableChars < availableBytes)
- {
- availableBytes = availableChars;
- }
-
- // To compute the upper bound, assume that all characters are ASCII characters at this point,
- // the boundary will be decreased for every non-ASCII character we encounter
- // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
- char* pStop = pTarget + availableBytes - 7;
-
- while (pTarget < pStop)
- {
- ch = *pSrc;
- pSrc++;
-
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- *pTarget = (char)ch;
- pTarget++;
-
- // get pSrc to be 2-byte aligned
- if ((unchecked((int)pSrc) & 0x1) != 0)
- {
- ch = *pSrc;
- pSrc++;
- if (ch > 0x7F)
- {
- goto LongCode;
- }
- *pTarget = (char)ch;
- pTarget++;
- }
-
- // get pSrc to be 4-byte aligned
- if ((unchecked((int)pSrc) & 0x2) != 0)
- {
- ch = *(ushort*)pSrc;
- if ((ch & 0x8080) != 0)
- {
- goto LongCodeWithMask16;
- }
-
- // Unfortunately, this is endianess sensitive
- if (BitConverter.IsLittleEndian)
- {
- *pTarget = (char)(ch & 0x7F);
- pSrc += 2;
- *(pTarget + 1) = (char)((ch >> 8) & 0x7F);
- pTarget += 2;
- }
- else
- {
- *pTarget = (char)((ch >> 8) & 0x7F);
- pSrc += 2;
- *(pTarget+1) = (char)(ch & 0x7F);
- pTarget += 2;
- }
- }
-
- // Run 8 characters at a time!
- while (pTarget < pStop)
- {
- ch = *(int*)pSrc;
- int chb = *(int*)(pSrc + 4);
- if (((ch | chb) & unchecked((int)0x80808080)) != 0)
- {
- goto LongCodeWithMask32;
- }
-
- // Unfortunately, this is endianess sensitive
- if (BitConverter.IsLittleEndian)
- {
- *pTarget = (char)(ch & 0x7F);
- *(pTarget + 1) = (char)((ch >> 8) & 0x7F);
- *(pTarget + 2) = (char)((ch >> 16) & 0x7F);
- *(pTarget + 3) = (char)((ch >> 24) & 0x7F);
- pSrc += 8;
- *(pTarget + 4) = (char)(chb & 0x7F);
- *(pTarget + 5) = (char)((chb >> 8) & 0x7F);
- *(pTarget + 6) = (char)((chb >> 16) & 0x7F);
- *(pTarget + 7) = (char)((chb >> 24) & 0x7F);
- pTarget += 8;
- }
- else
- {
- *pTarget = (char)((ch >> 24) & 0x7F);
- *(pTarget+1) = (char)((ch >> 16) & 0x7F);
- *(pTarget+2) = (char)((ch >> 8) & 0x7F);
- *(pTarget+3) = (char)(ch & 0x7F);
- pSrc += 8;
- *(pTarget+4) = (char)((chb >> 24) & 0x7F);
- *(pTarget+5) = (char)((chb >> 16) & 0x7F);
- *(pTarget+6) = (char)((chb >> 8) & 0x7F);
- *(pTarget+7) = (char)(chb & 0x7F);
- pTarget += 8;
- }
- }
- break;
-
- LongCodeWithMask32:
- if (BitConverter.IsLittleEndian)
- {
- ch &= 0xFF;
- }
- else
- {
- // be careful about the sign extension
- ch = (int)(((uint)ch) >> 16);
- }
- LongCodeWithMask16:
- if (BitConverter.IsLittleEndian)
- {
- ch &= 0xFF;
- }
- else
- {
- ch = (int)(((uint)ch) >> 8);
- }
- pSrc++;
- if (ch <= 0x7F)
- {
- *pTarget = (char)ch;
- pTarget++;
- continue;
- }
-
- LongCode:
- int chc = *pSrc;
- pSrc++;
-
- if (
- // bit 6 has to be zero
- (ch & 0x40) == 0 ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (chc & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
-
- chc &= 0x3F;
-
- // start a new long code
- if ((ch & 0x20) != 0)
- {
- // fold the first two bytes together
- chc |= (ch & 0x0F) << 6;
-
- if ((ch & 0x10) != 0)
- {
- // 4 byte encoding - surrogate
- ch = *pSrc;
- if (
- // check that bit 4 is zero, the non-shortest form of surrogate
- // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
- !InRange(chc >> 4, 0x01, 0x10) ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
-
- chc = (chc << 6) | (ch & 0x3F);
-
- ch = *(pSrc + 1);
- // we are expecting to see trailing bytes like 10vvvvvv
- if ((ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
- pSrc += 2;
-
- ch = (chc << 6) | (ch & 0x3F);
-
- *pTarget = (char)(((ch >> 10) & 0x7FF) +
- unchecked((short)(CharUnicodeInfo.HIGH_SURROGATE_START - (0x10000 >> 10))));
- pTarget++;
-
- ch = (ch & 0x3FF) +
- unchecked((short)(CharUnicodeInfo.LOW_SURROGATE_START));
-
- // extra byte, we're already planning 2 chars for 2 of these bytes,
- // but the big loop is testing the target against pStop, so we need
- // to subtract 2 more or we risk overrunning the input. Subtract
- // one here and one below.
- pStop--;
- }
- else
- {
- // 3 byte encoding
- ch = *pSrc;
- if (
- // check for non-shortest form of 3 byte seq
- (chc & (0x1F << 5)) == 0 ||
- // Can't have surrogates here.
- (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
- // we are expecting to see trailing bytes like 10vvvvvv
- (ch & unchecked((sbyte)0xC0)) != 0x80)
- {
- goto BadLongCode;
- }
- pSrc++;
-
- ch = (chc << 6) | (ch & 0x3F);
-
- // extra byte, we're only expecting 1 char for each of these 3 bytes,
- // but the loop is testing the target (not source) against pStop, so
- // we need to subtract 2 more or we risk overrunning the input.
- // Subtract 1 here and one more below
- pStop--;
- }
- }
- else
- {
- // 2 byte encoding
-
- ch &= 0x1F;
-
- // check for non-shortest form
- if (ch <= 1)
- {
- goto BadLongCode;
- }
- ch = (ch << 6) | chc;
- }
-
- *pTarget = (char)ch;
- pTarget++;
-
- // extra byte, we're only expecting 1 char for each of these 2 bytes,
- // but the loop is testing the target (not source) against pStop.
- // subtract an extra count from pStop so that we don't overrun the input.
- pStop--;
- }
-#endif // FASTLOOP
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe int GetCharCountCommon(byte* pBytes, int byteCount)
+ {
+ // Common helper method for all non-DecoderNLS entry points to GetCharCount.
+ // A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
- Debug.Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetChars]pTarget <= pAllocatedBufferEnd");
+ Debug.Assert(byteCount >= 0, "Caller should't specify negative length buffer.");
+ Debug.Assert(pBytes != null || byteCount == 0, "Input pointer shouldn't be null if non-zero length specified.");
- // no pending bits at this point
- ch = 0;
- continue;
+ // First call into the fast path.
+ // Don't bother providing a fallback mechanism; our fast path doesn't use it.
- BadLongCode:
- pSrc -= 2;
- ch = 0;
- continue;
- }
+ int totalCharCount = GetCharCountFast(pBytes, byteCount, fallback: null, out int bytesConsumed);
- if (ch != 0 && (baseDecoder == null || baseDecoder.MustFlush))
+ if (bytesConsumed != byteCount)
{
- // Have to do fallback for invalid bytes
- if (fallback == null)
- {
- if (baseDecoder == null)
- fallback = this.decoderFallback.CreateFallbackBuffer();
- else
- fallback = baseDecoder.FallbackBuffer;
- fallback.InternalInitialize(bytes, pAllocatedBufferEnd);
- }
-
- // That'll back us up the appropriate # of bytes if we didn't get anywhere
- pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be en-registered
- pTargetForFallback = pTarget; // Avoid passing pTarget by reference to allow it to be en-registered
- bool fallbackResult = FallbackInvalidByteSequence(ref pSrcForFallback, ch, fallback, ref pTargetForFallback);
- pSrc = pSrcForFallback;
- pTarget = pTargetForFallback;
+ // If there's still data remaining in the source buffer, go down the fallback path.
+ // We need to check for integer overflow since the fallback could change the required
+ // output count in unexpected ways.
- if (!fallbackResult)
+ totalCharCount += GetCharCountWithFallback(pBytes, byteCount, bytesConsumed);
+ if (totalCharCount < 0)
{
- Debug.Assert(pSrc >= bytes || pTarget == chars,
- "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer while flushing");
-
- // Ran out of buffer space
- // Need to throw an exception?
- fallback.InternalReset();
- ThrowCharsOverflow(baseDecoder, pTarget == chars);
+ ThrowConversionOverflow();
}
- Debug.Assert(pSrc >= bytes,
- "[UTF8Encoding.GetChars]Expected flushing invalid byte sequence to have remained within the byte array");
- ch = 0;
}
- if (baseDecoder != null)
- {
- UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
+ return totalCharCount;
+ }
- // If we're storing flush data we expect all bits to be used or else
- // we're stuck in the middle of a conversion
- Debug.Assert(!baseDecoder.MustFlush || ch == 0 || !baseDecoder._throwOnOverflow,
- "[UTF8Encoding.GetChars]Expected no must flush or no left over bits or no throw on overflow.");
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // called directly by GetCharCountCommon
+ private protected sealed override unsafe int GetCharCountFast(byte* pBytes, int bytesLength, DecoderFallback fallback, out int bytesConsumed)
+ {
+ // The number of UTF-16 code units will never exceed the number of UTF-8 code units,
+ // so the addition at the end of this method will not overflow.
- // Remember our leftover bits.
- decoder.bits = ch;
+ byte* ptrToFirstInvalidByte = Utf8Utility.GetPointerToFirstInvalidByte(pBytes, bytesLength, out int utf16CodeUnitCountAdjustment, out _);
- baseDecoder._bytesUsed = (int)(pSrc - bytes);
- }
+ int tempBytesConsumed = (int)(ptrToFirstInvalidByte - pBytes);
+ bytesConsumed = tempBytesConsumed;
- // Shouldn't have anything in fallback buffer for GetChars
- // (don't have to check _throwOnOverflow for chars)
- Debug.Assert(fallback == null || fallback.Remaining == 0,
- "[UTF8Encoding.GetChars]Expected empty fallback buffer at end");
-
- return PtrDiff(pTarget, chars);
+ return tempBytesConsumed + utf16CodeUnitCountAdjustment;
}
- // During GetChars we had an invalid byte sequence
- // pSrc is backed up to the start of the bad sequence if we didn't have room to
- // fall it back. Otherwise pSrc remains where it is.
- private unsafe bool FallbackInvalidByteSequence(
- ref byte* pSrc, int ch, DecoderFallbackBuffer fallback, ref char* pTarget)
+ public override Decoder GetDecoder()
{
- // Get our byte[]
- byte* pStart = pSrc;
- byte[] bytesUnknown = GetBytesUnknown(ref pStart, ch);
-
- // Do the actual fallback
- if (!fallback.InternalFallback(bytesUnknown, pSrc, ref pTarget))
- {
- // Oops, it failed, back up to pStart
- pSrc = pStart;
- return false;
- }
-
- // It worked
- return true;
+ return new DecoderNLS(this);
}
- // During GetCharCount we had an invalid byte sequence
- // pSrc is used to find the index that points to the invalid bytes,
- // however the byte[] contains the fallback bytes (in case the index is -1)
- private unsafe int FallbackInvalidByteSequence(
- byte* pSrc, int ch, DecoderFallbackBuffer fallback)
+
+ public override Encoder GetEncoder()
{
- // Calling GetBytesUnknown can adjust the pSrc pointer but we need to pass the pointer before the adjustment
- // to fallback.InternalFallback. The input pSrc to fallback.InternalFallback will only be used to calculate the
- // index inside bytesUnknown and if we pass the adjusted pointer we can end up with negative index values.
- // We store the original pSrc in pOriginalSrc and then pass pOriginalSrc to fallback.InternalFallback.
- byte* pOriginalSrc = pSrc;
-
- // Get our byte[]
- byte[] bytesUnknown = GetBytesUnknown(ref pSrc, ch);
-
- // Do the actual fallback
- int count = fallback.InternalFallback(bytesUnknown, pOriginalSrc);
-
- // # of fallback chars expected.
- // Note that we only get here for "long" sequences, and have already unreserved
- // the count that we prereserved for the input bytes
- return count;
+ return new EncoderNLS(this);
}
- // Note that some of these bytes may have come from a previous fallback, so we cannot
- // just decrement the pointer and use the values we read. In those cases we have
- // to regenerate the original values.
- private unsafe byte[] GetBytesUnknown(ref byte* pSrc, int ch)
- {
- // Get our byte[]
- byte[] bytesUnknown = null;
+ //
+ // Beginning of methods used by shared fallback logic.
+ //
- // See if it was a plain char
- // (have to check >= 0 because we have all sorts of wierd bit flags)
- if (ch < 0x100 && ch >= 0)
- {
- pSrc--;
- bytesUnknown = new byte[] { unchecked((byte)ch) };
- }
- // See if its an unfinished 2 byte sequence
- else if ((ch & (SupplimentarySeq | ThreeByteSeq)) == 0)
- {
- pSrc--;
- bytesUnknown = new byte[] { unchecked((byte)((ch & 0x1F) | 0xc0)) };
- }
- // So now we're either 2nd byte of 3 or 4 byte sequence or
- // we hit a non-trail byte or we ran out of space for 3rd byte of 4 byte sequence
- // 1st check if its a 4 byte sequence
- else if ((ch & SupplimentarySeq) != 0)
- {
- // 3rd byte of 4 byte sequence?
- if ((ch & (FinalByte >> 6)) != 0)
- {
- // 3rd byte of 4 byte sequence
- pSrc -= 3;
- bytesUnknown = new byte[] {
- unchecked((byte)(((ch >> 12) & 0x07) | 0xF0)),
- unchecked((byte)(((ch >> 6) & 0x3F) | 0x80)),
- unchecked((byte)(((ch) & 0x3F) | 0x80)) };
- }
- else if ((ch & (FinalByte >> 12)) != 0)
- {
- // 2nd byte of a 4 byte sequence
- pSrc -= 2;
- bytesUnknown = new byte[] {
- unchecked((byte)(((ch >> 6) & 0x07) | 0xF0)),
- unchecked((byte)(((ch) & 0x3F) | 0x80)) };
- }
- else
- {
- // 4th byte of a 4 byte sequence
- pSrc--;
- bytesUnknown = new byte[] { unchecked((byte)(((ch) & 0x07) | 0xF0)) };
- }
- }
- else
- {
- // 2nd byte of 3 byte sequence?
- if ((ch & (FinalByte >> 6)) != 0)
- {
- // So its 2nd byte of a 3 byte sequence
- pSrc -= 2;
- bytesUnknown = new byte[] {
- unchecked((byte)(((ch >> 6) & 0x0F) | 0xE0)), unchecked ((byte)(((ch) & 0x3F) | 0x80)) };
- }
- else
- {
- // 1st byte of a 3 byte sequence
- pSrc--;
- bytesUnknown = new byte[] { unchecked((byte)(((ch) & 0x0F) | 0xE0)) };
- }
- }
+ internal sealed override bool TryGetByteCount(Rune value, out int byteCount)
+ {
+ // All well-formed Rune instances can be converted to 1..4 UTF-8 code units.
- return bytesUnknown;
+ byteCount = value.Utf8SequenceLength;
+ return true;
}
-
- public override Decoder GetDecoder()
+ internal sealed override OperationStatus EncodeRune(Rune value, Span<byte> bytes, out int bytesWritten)
{
- return new UTF8Decoder(this);
- }
+ // All well-formed Rune instances can be encoded as 1..4 UTF-8 code units.
+ // If there's an error, it's because the destination was too small.
+ return value.TryEncodeToUtf8(bytes, out bytesWritten) ? OperationStatus.Done : OperationStatus.DestinationTooSmall;
+ }
- public override Encoder GetEncoder()
+ internal sealed override OperationStatus DecodeFirstRune(ReadOnlySpan<byte> bytes, out Rune value, out int bytesConsumed)
{
- return new UTF8Encoder(this);
+ return Rune.DecodeFromUtf8(bytes, out value, out bytesConsumed);
}
+ //
+ // End of methods used by shared fallback logic.
+ //
public override int GetMaxByteCount(int charCount)
{
return this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
UTF8_CODEPAGE + (_emitUTF8Identifier ? 1 : 0);
}
-
- private sealed class UTF8Encoder : EncoderNLS
- {
- // We must save a high surrogate value until the next call, looking
- // for a low surrogate value.
- internal int surrogateChar;
-
- public UTF8Encoder(UTF8Encoding encoding) : base(encoding)
- {
- // base calls reset
- }
-
- public override void Reset()
-
- {
- this.surrogateChar = 0;
- if (_fallbackBuffer != null)
- _fallbackBuffer.Reset();
- }
-
- // Anything left in our encoder?
- internal override bool HasState
- {
- get
- {
- return (this.surrogateChar != 0);
- }
- }
- }
-
- private sealed class UTF8Decoder : DecoderNLS
- {
- // We'll need to remember the previous information. See the comments around definition
- // of FinalByte for details.
- internal int bits;
-
- public UTF8Decoder(UTF8Encoding encoding) : base(encoding)
- {
- // base calls reset
- }
-
- public override void Reset()
- {
- this.bits = 0;
- if (_fallbackBuffer != null)
- _fallbackBuffer.Reset();
- }
-
- // Anything left in our decoder?
- internal override bool HasState
- {
- get
- {
- return (this.bits != 0);
- }
- }
- }
}
}