1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Diagnostics;
6 using System.Globalization;
7 using System.Threading;
11 public abstract class DecoderFallback
13 private static DecoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?"
14 private static DecoderFallback s_exceptionFallback;
16 public static DecoderFallback ReplacementFallback =>
17 s_replacementFallback ?? Interlocked.CompareExchange(ref s_replacementFallback, new DecoderReplacementFallback(), null) ?? s_replacementFallback;
20 public static DecoderFallback ExceptionFallback =>
21 s_exceptionFallback ?? Interlocked.CompareExchange<DecoderFallback>(ref s_exceptionFallback, new DecoderExceptionFallback(), null) ?? s_exceptionFallback;
25 // Return the appropriate unicode string alternative to the character that need to fall back.
26 // Most implementations will be:
27 // return new MyCustomDecoderFallbackBuffer(this);
29 public abstract DecoderFallbackBuffer CreateFallbackBuffer();
31 // Maximum number of characters that this instance of this fallback could return
33 public abstract int MaxCharCount { get; }
37 public abstract class DecoderFallbackBuffer
39 // Most implementations will probably need an implementation-specific constructor
41 // internal methods that cannot be overridden that let us do our fallback thing
42 // These wrap the internal methods so that we can check for people doing stuff that's incorrect
44 public abstract bool Fallback(byte[] bytesUnknown, int index);
48 public abstract char GetNextChar();
50 // Back up a character
52 public abstract bool MovePrevious();
54 // How many chars left in this fallback?
56 public abstract int Remaining { get; }
60 public virtual void Reset()
62 while (GetNextChar() != (char)0) ;
65 // Internal items to help us figure out what we're doing as far as error messages, etc.
66 // These help us with our performance and messages internally
67 internal unsafe byte* byteStart;
68 internal unsafe char* charEnd;
71 internal unsafe void InternalReset()
77 // Set the above values
78 // This can't be part of the constructor because DecoderFallbacks would have to know how to implement these.
79 internal unsafe void InternalInitialize(byte* byteStart, char* charEnd)
81 this.byteStart = byteStart;
82 this.charEnd = charEnd;
85 // Fallback the current byte by sticking it into the remaining char buffer.
86 // This can only be called by our encodings (other have to use the public fallback methods), so
87 // we can use our DecoderNLS here too (except we don't).
88 // Returns true if we are successful, false if we can't fallback the character (no buffer space)
89 // So caller needs to throw buffer space if return false.
90 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
91 // array, and we might need the index, hence the byte*
92 // Don't touch ref chars unless we succeed
93 internal unsafe virtual bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars)
95 Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
97 // See if there's a fallback character and we have an output buffer then copy our string.
98 if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
100 // Copy the chars to our output
102 char* charTemp = chars;
103 bool bHighSurrogate = false;
104 while ((ch = GetNextChar()) != 0)
106 // Make sure no mixed up surrogates
107 if (Char.IsSurrogate(ch))
109 if (Char.IsHighSurrogate(ch))
113 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
114 bHighSurrogate = true;
119 if (bHighSurrogate == false)
120 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
121 bHighSurrogate = false;
125 if (charTemp >= charEnd)
134 // Need to make sure that bHighSurrogate isn't true
136 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
138 // Now we aren't going to be false, so its OK to update chars
145 // This version just counts the fallback and doesn't actually copy anything.
146 internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes)
147 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
148 // array, and we might need the index, hence the byte*
150 Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
152 // See if there's a fallback character and we have an output buffer then copy our string.
153 if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
158 bool bHighSurrogate = false;
159 while ((ch = GetNextChar()) != 0)
161 // Make sure no mixed up surrogates
162 if (Char.IsSurrogate(ch))
164 if (Char.IsHighSurrogate(ch))
168 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
169 bHighSurrogate = true;
174 if (bHighSurrogate == false)
175 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
176 bHighSurrogate = false;
183 // Need to make sure that bHighSurrogate isn't true
185 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
190 // If no fallback return 0
194 // private helper methods
195 internal void ThrowLastBytesRecursive(byte[] bytesUnknown)
197 // Create a string representation of our bytes.
198 StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3);
200 for (i = 0; i < bytesUnknown.Length && i < 20; i++)
202 if (strBytes.Length > 0)
203 strBytes.Append(' ');
204 strBytes.AppendFormat(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]);
206 // In case the string's really long
208 strBytes.Append(" ...");
210 // Throw it, using our complete bytes
211 throw new ArgumentException(
212 SR.Format(SR.Argument_RecursiveFallbackBytes,
213 strBytes.ToString()), nameof(bytesUnknown));