2a56706b988e1fed27e25a2a801da5fc739e1cd0
[platform/upstream/coreclr.git] / src / mscorlib / src / 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 //
6
7 using System;
8 using System.Security;
9 using System.Threading;
10 using System.Globalization;
11 using System.Diagnostics;
12 using System.Diagnostics.Contracts;
13
14 namespace System.Text
15 {
16     public abstract class DecoderFallback
17     {
18         internal bool bIsMicrosoftBestFitFallback = false;
19
20         private static volatile DecoderFallback replacementFallback; // Default fallback, uses no best fit & "?"
21         private static volatile DecoderFallback exceptionFallback;
22
23         // Private object for locking instead of locking on a internal type for SQL reliability work.
24         private static Object s_InternalSyncObject;
25         private static Object InternalSyncObject
26         {
27             get
28             {
29                 if (s_InternalSyncObject == null)
30                 {
31                     Object o = new Object();
32                     Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
33                 }
34                 return s_InternalSyncObject;
35             }
36         }
37
38         // Get each of our generic fallbacks.
39
40         public static DecoderFallback ReplacementFallback
41         {
42             get
43             {
44                 if (replacementFallback == null)
45                     lock (InternalSyncObject)
46                         if (replacementFallback == null)
47                             replacementFallback = new DecoderReplacementFallback();
48
49                 return replacementFallback;
50             }
51         }
52
53
54         public static DecoderFallback ExceptionFallback
55         {
56             get
57             {
58                 if (exceptionFallback == null)
59                     lock (InternalSyncObject)
60                         if (exceptionFallback == null)
61                             exceptionFallback = new DecoderExceptionFallback();
62
63                 return exceptionFallback;
64             }
65         }
66
67         // Fallback
68         //
69         // Return the appropriate unicode string alternative to the character that need to fall back.
70         // Most implimentations will be:
71         //      return new MyCustomDecoderFallbackBuffer(this);
72
73         public abstract DecoderFallbackBuffer CreateFallbackBuffer();
74
75         // Maximum number of characters that this instance of this fallback could return
76
77         public abstract int MaxCharCount { get; }
78     }
79
80
81     public abstract class DecoderFallbackBuffer
82     {
83         // Most implimentations will probably need an implimenation-specific constructor
84
85         // internal methods that cannot be overriden that let us do our fallback thing
86         // These wrap the internal methods so that we can check for people doing stuff that's incorrect
87
88         public abstract bool Fallback(byte[] bytesUnknown, int index);
89
90         // Get next character
91
92         public abstract char GetNextChar();
93
94         // Back up a character
95
96         public abstract bool MovePrevious();
97
98         // How many chars left in this fallback?
99
100         public abstract int Remaining { get; }
101
102         // Clear the buffer
103
104         public virtual void Reset()
105         {
106             while (GetNextChar() != (char)0) ;
107         }
108
109         // Internal items to help us figure out what we're doing as far as error messages, etc.
110         // These help us with our performance and messages internally
111         internal unsafe byte* byteStart;
112         internal unsafe char* charEnd;
113
114         // Internal Reset
115         internal unsafe void InternalReset()
116         {
117             byteStart = null;
118             Reset();
119         }
120
121         // Set the above values
122         // This can't be part of the constructor because DecoderFallbacks would have to know how to impliment these.
123         internal unsafe void InternalInitialize(byte* byteStart, char* charEnd)
124         {
125             this.byteStart = byteStart;
126             this.charEnd = charEnd;
127         }
128
129         // Fallback the current byte by sticking it into the remaining char buffer.
130         // This can only be called by our encodings (other have to use the public fallback methods), so
131         // we can use our DecoderNLS here too (except we don't).
132         // Returns true if we are successful, false if we can't fallback the character (no buffer space)
133         // So caller needs to throw buffer space if return false.
134         // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
135         // array, and we might need the index, hence the byte*
136         // Don't touch ref chars unless we succeed
137         internal unsafe virtual bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars)
138         {
139             // Copy bytes to array (slow, but right now that's what we get to do.
140             //  byte[] bytesUnknown = new byte[count];
141             //            for (int i = 0; i < count; i++)
142             //                bytesUnknown[i] = *(bytes++);
143
144             Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
145
146             // See if there's a fallback character and we have an output buffer then copy our string.
147             if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
148             {
149                 // Copy the chars to our output
150                 char ch;
151                 char* charTemp = chars;
152                 bool bHighSurrogate = false;
153                 while ((ch = GetNextChar()) != 0)
154                 {
155                     // Make sure no mixed up surrogates
156                     if (Char.IsSurrogate(ch))
157                     {
158                         if (Char.IsHighSurrogate(ch))
159                         {
160                             // High Surrogate
161                             if (bHighSurrogate)
162                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
163                             bHighSurrogate = true;
164                         }
165                         else
166                         {
167                             // Low surrogate
168                             if (bHighSurrogate == false)
169                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
170                             bHighSurrogate = false;
171                         }
172                     }
173
174                     if (charTemp >= charEnd)
175                     {
176                         // No buffer space
177                         return false;
178                     }
179
180                     *(charTemp++) = ch;
181                 }
182
183                 // Need to make sure that bHighSurrogate isn't true
184                 if (bHighSurrogate)
185                     throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
186
187                 // Now we aren't going to be false, so its OK to update chars
188                 chars = charTemp;
189             }
190
191             return true;
192         }
193
194         // This version just counts the fallback and doesn't actually copy anything.
195         internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes)
196         // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
197         // array, and we might need the index, hence the byte*
198         {
199             // Copy bytes to array (slow, but right now that's what we get to do.
200             //            byte[] bytesUnknown = new byte[count];
201             //            for (int i = 0; i < count; i++)
202             //              bytesUnknown[i] = *(bytes++);
203
204             Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
205
206             // See if there's a fallback character and we have an output buffer then copy our string.
207             if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
208             {
209                 int count = 0;
210
211                 char ch;
212                 bool bHighSurrogate = false;
213                 while ((ch = GetNextChar()) != 0)
214                 {
215                     // Make sure no mixed up surrogates
216                     if (Char.IsSurrogate(ch))
217                     {
218                         if (Char.IsHighSurrogate(ch))
219                         {
220                             // High Surrogate
221                             if (bHighSurrogate)
222                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
223                             bHighSurrogate = true;
224                         }
225                         else
226                         {
227                             // Low surrogate
228                             if (bHighSurrogate == false)
229                                 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
230                             bHighSurrogate = false;
231                         }
232                     }
233
234                     count++;
235                 }
236
237                 // Need to make sure that bHighSurrogate isn't true
238                 if (bHighSurrogate)
239                     throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
240
241                 return count;
242             }
243
244             // If no fallback return 0
245             return 0;
246         }
247
248         // private helper methods
249         internal void ThrowLastBytesRecursive(byte[] bytesUnknown)
250         {
251             // Create a string representation of our bytes.
252             StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3);
253             int i;
254             for (i = 0; i < bytesUnknown.Length && i < 20; i++)
255             {
256                 if (strBytes.Length > 0)
257                     strBytes.Append(" ");
258                 strBytes.Append(String.Format(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]));
259             }
260             // In case the string's really long
261             if (i == 20)
262                 strBytes.Append(" ...");
263
264             // Throw it, using our complete bytes
265             throw new ArgumentException(
266                 SR.Format(SR.Argument_RecursiveFallbackBytes, strBytes.ToString()), nameof(bytesUnknown));
267         }
268     }
269 }