Merge pull request #12748 from helloguo/NUMASupportInitialize
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Text / DecoderFallback.cs
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.
4
5 using System.Diagnostics;
6 using System.Globalization;
7 using System.Threading;
8
9 namespace System.Text
10 {
11     public abstract class DecoderFallback
12     {
13         private static DecoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?"
14         private static DecoderFallback s_exceptionFallback;
15
16         public static DecoderFallback ReplacementFallback =>
17             s_replacementFallback ?? Interlocked.CompareExchange(ref s_replacementFallback, new DecoderReplacementFallback(), null) ?? s_replacementFallback;        
18
19
20         public static DecoderFallback ExceptionFallback =>
21             s_exceptionFallback ?? Interlocked.CompareExchange<DecoderFallback>(ref s_exceptionFallback, new DecoderExceptionFallback(), null) ?? s_exceptionFallback;
22
23         // Fallback
24         //
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);
28
29         public abstract DecoderFallbackBuffer CreateFallbackBuffer();
30
31         // Maximum number of characters that this instance of this fallback could return
32
33         public abstract int MaxCharCount { get; }
34     }
35
36
37     public abstract class DecoderFallbackBuffer
38     {
39         // Most implementations will probably need an implementation-specific constructor
40
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
43
44         public abstract bool Fallback(byte[] bytesUnknown, int index);
45
46         // Get next character
47
48         public abstract char GetNextChar();
49
50         // Back up a character
51
52         public abstract bool MovePrevious();
53
54         // How many chars left in this fallback?
55
56         public abstract int Remaining { get; }
57
58         // Clear the buffer
59
60         public virtual void Reset()
61         {
62             while (GetNextChar() != (char)0) ;
63         }
64
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;
69
70         // Internal Reset
71         internal unsafe void InternalReset()
72         {
73             byteStart = null;
74             Reset();
75         }
76
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)
80         {
81             this.byteStart = byteStart;
82             this.charEnd = charEnd;
83         }
84
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)
94         {
95             Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
96
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)))
99             {
100                 // Copy the chars to our output
101                 char ch;
102                 char* charTemp = chars;
103                 bool bHighSurrogate = false;
104                 while ((ch = GetNextChar()) != 0)
105                 {
106                     // Make sure no mixed up surrogates
107                     if (Char.IsSurrogate(ch))
108                     {
109                         if (Char.IsHighSurrogate(ch))
110                         {
111                             // High Surrogate
112                             if (bHighSurrogate)
113                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
114                             bHighSurrogate = true;
115                         }
116                         else
117                         {
118                             // Low surrogate
119                             if (bHighSurrogate == false)
120                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
121                             bHighSurrogate = false;
122                         }
123                     }
124
125                     if (charTemp >= charEnd)
126                     {
127                         // No buffer space
128                         return false;
129                     }
130
131                     *(charTemp++) = ch;
132                 }
133
134                 // Need to make sure that bHighSurrogate isn't true
135                 if (bHighSurrogate)
136                     throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
137
138                 // Now we aren't going to be false, so its OK to update chars
139                 chars = charTemp;
140             }
141
142             return true;
143         }
144
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*
149         {
150             Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
151
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)))
154             {
155                 int count = 0;
156
157                 char ch;
158                 bool bHighSurrogate = false;
159                 while ((ch = GetNextChar()) != 0)
160                 {
161                     // Make sure no mixed up surrogates
162                     if (Char.IsSurrogate(ch))
163                     {
164                         if (Char.IsHighSurrogate(ch))
165                         {
166                             // High Surrogate
167                             if (bHighSurrogate)
168                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
169                             bHighSurrogate = true;
170                         }
171                         else
172                         {
173                             // Low surrogate
174                             if (bHighSurrogate == false)
175                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
176                             bHighSurrogate = false;
177                         }
178                     }
179
180                     count++;
181                 }
182
183                 // Need to make sure that bHighSurrogate isn't true
184                 if (bHighSurrogate)
185                     throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
186
187                 return count;
188             }
189
190             // If no fallback return 0
191             return 0;
192         }
193
194         // private helper methods
195         internal void ThrowLastBytesRecursive(byte[] bytesUnknown)
196         {
197             // Create a string representation of our bytes.
198             StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3);
199             int i;
200             for (i = 0; i < bytesUnknown.Length && i < 20; i++)
201             {
202                 if (strBytes.Length > 0)
203                     strBytes.Append(' ');
204                 strBytes.AppendFormat(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]);
205             }
206             // In case the string's really long
207             if (i == 20)
208                 strBytes.Append(" ...");
209
210             // Throw it, using our complete bytes
211             throw new ArgumentException(
212                 SR.Format(SR.Argument_RecursiveFallbackBytes,
213                     strBytes.ToString()), nameof(bytesUnknown));
214         }
215     }
216 }