| File: System\Text\EncoderFallbackBufferHelper.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Encoding.CodePages\src\System.Text.Encoding.CodePages.csproj (System.Text.Encoding.CodePages) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; namespace System.Text { internal struct EncoderFallbackBufferHelper { public unsafe EncoderFallbackBufferHelper(EncoderFallbackBuffer? fallbackBuffer) { _fallbackBuffer = fallbackBuffer; bFallingBack = bUsedEncoder = setEncoder = false; iRecursionCount = 0; charEnd = charStart = null; encoder = null; } // Internal items to help us figure out what we're doing as far as error messages, etc. // These help us with our performance and messages internally internal unsafe char* charStart; internal unsafe char* charEnd; internal EncoderNLS? encoder; internal bool setEncoder; internal bool bUsedEncoder; internal bool bFallingBack; internal int iRecursionCount; private const int iMaxRecursion = 250; private readonly EncoderFallbackBuffer? _fallbackBuffer; // Internal Reset // For example, what if someone fails a conversion and wants to reset one of our fallback buffers? internal unsafe void InternalReset() { Debug.Assert(_fallbackBuffer != null); charStart = null; bFallingBack = false; iRecursionCount = 0; _fallbackBuffer!.Reset(); } // Set the above values // This can't be part of the constructor because EncoderFallbacks would have to know how to implement these. internal unsafe void InternalInitialize(char* _charStart, char* _charEnd, EncoderNLS? _encoder, bool _setEncoder) { charStart = _charStart; charEnd = _charEnd; encoder = _encoder; setEncoder = _setEncoder; bUsedEncoder = false; bFallingBack = false; iRecursionCount = 0; } internal char InternalGetNextChar() { Debug.Assert(_fallbackBuffer != null); char ch = _fallbackBuffer!.GetNextChar(); bFallingBack = (ch != 0); if (ch == 0) iRecursionCount = 0; return ch; } // Fallback the current character using the remaining buffer and encoder if necessary // This can only be called by our encodings (other have to use the public fallback methods), so // we can use our EncoderNLS here too. // setEncoder is true if we're calling from a GetBytes method, false if we're calling from a GetByteCount // // Note that this could also change the contents of encoder, which is the same // object that the caller is using, so the caller could mess up the encoder for us // if they aren't careful. internal unsafe bool InternalFallback(char ch, ref char* chars) { // Shouldn't have null charStart or fallback buffer Debug.Assert(charStart != null, "[EncoderFallback.InternalFallbackBuffer]Fallback buffer is not initialized"); Debug.Assert(_fallbackBuffer != null); // Get our index, remember chars was preincremented to point at next char, so have to decrement int index = (int)(chars - charStart) - 1; // See if it was a high surrogate if (char.IsHighSurrogate(ch)) { // See if there's a low surrogate to go with it if (chars >= charEnd) { // Nothing left in input buffer // No input, return 0 if mustflush is false if (encoder != null && !encoder.MustFlush) { // Done, nothing to fallback if (setEncoder) { bUsedEncoder = true; encoder.charLeftOver = ch; } bFallingBack = false; return false; } } else { // Might have a low surrogate char cNext = *chars; if (char.IsLowSurrogate(cNext)) { // If already falling back then fail if (bFallingBack && iRecursionCount++ > iMaxRecursion) ThrowLastCharRecursive(char.ConvertToUtf32(ch, cNext)); // Next is a surrogate, add it as surrogate pair, and increment chars chars++; bFallingBack = _fallbackBuffer!.Fallback(ch, cNext, index); return bFallingBack; } // Next isn't a low surrogate, just fallback the high surrogate } } // If already falling back then fail if (bFallingBack && iRecursionCount++ > iMaxRecursion) ThrowLastCharRecursive((int)ch); // Fall back our char bFallingBack = _fallbackBuffer!.Fallback(ch, index); return bFallingBack; } // private helper methods internal static void ThrowLastCharRecursive(int charRecursive) { // Throw it, using our complete character throw new ArgumentException(SR.Format(SR.Argument_RecursiveFallback, charRecursive), "chars"); } } }