c3b9f47284d1997746215ba3c73487e4c61f9b29
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Text / EncoderFallback.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;
6 using System.Security;
7 using System.Threading;
8 using System.Diagnostics;
9 using System.Diagnostics.Contracts;
10
11 namespace System.Text
12 {
13     public abstract class EncoderFallback
14     {
15         // disable csharp compiler warning #0414: field assigned unused value
16 #pragma warning disable 0414
17         internal bool bIsMicrosoftBestFitFallback = false;
18 #pragma warning restore 0414
19
20         private static volatile EncoderFallback replacementFallback; // Default fallback, uses no best fit & "?"
21         private static volatile EncoderFallback exceptionFallback;
22
23         // Private object for locking instead of locking on a public 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 EncoderFallback ReplacementFallback
41         {
42             get
43             {
44                 if (replacementFallback == null)
45                     lock (InternalSyncObject)
46                         if (replacementFallback == null)
47                             replacementFallback = new EncoderReplacementFallback();
48
49                 return replacementFallback;
50             }
51         }
52
53
54         public static EncoderFallback ExceptionFallback
55         {
56             get
57             {
58                 if (exceptionFallback == null)
59                     lock (InternalSyncObject)
60                         if (exceptionFallback == null)
61                             exceptionFallback = new EncoderExceptionFallback();
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 MyCustomEncoderFallbackBuffer(this);
72
73         public abstract EncoderFallbackBuffer 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 EncoderFallbackBuffer
82     {
83         // Most implementations will probably need an implemenation-specific constructor
84
85         // Public 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 is incorrect
87
88         public abstract bool Fallback(char charUnknown, int index);
89
90         public abstract bool Fallback(char charUnknownHigh, char charUnknownLow, int index);
91
92         // Get next character
93
94         public abstract char GetNextChar();
95
96         // Back up a character
97
98         public abstract bool MovePrevious();
99
100         // How many chars left in this fallback?
101
102         public abstract int Remaining { get; }
103
104         // Not sure if this should be public or not.
105         // Clear the buffer
106
107         public virtual void Reset()
108         {
109             while (GetNextChar() != (char)0) ;
110         }
111
112         // Internal items to help us figure out what we're doing as far as error messages, etc.
113         // These help us with our performance and messages internally
114         internal unsafe char* charStart;
115         internal unsafe char* charEnd;
116         internal EncoderNLS encoder;
117         internal bool setEncoder;
118         internal bool bUsedEncoder;
119         internal bool bFallingBack = false;
120         internal int iRecursionCount = 0;
121         private const int iMaxRecursion = 250;
122
123         // Internal Reset
124         // For example, what if someone fails a conversion and wants to reset one of our fallback buffers?
125         internal unsafe void InternalReset()
126         {
127             charStart = null;
128             bFallingBack = false;
129             iRecursionCount = 0;
130             Reset();
131         }
132
133         // Set the above values
134         // This can't be part of the constructor because EncoderFallbacks would have to know how to impliment these.
135         internal unsafe void InternalInitialize(char* charStart, char* charEnd, EncoderNLS encoder, bool setEncoder)
136         {
137             this.charStart = charStart;
138             this.charEnd = charEnd;
139             this.encoder = encoder;
140             this.setEncoder = setEncoder;
141             this.bUsedEncoder = false;
142             this.bFallingBack = false;
143             this.iRecursionCount = 0;
144         }
145
146         internal char InternalGetNextChar()
147         {
148             char ch = GetNextChar();
149             bFallingBack = (ch != 0);
150             if (ch == 0) iRecursionCount = 0;
151             return ch;
152         }
153
154         // Fallback the current character using the remaining buffer and encoder if necessary
155         // This can only be called by our encodings (other have to use the public fallback methods), so
156         // we can use our EncoderNLS here too.
157         // setEncoder is true if we're calling from a GetBytes method, false if we're calling from a GetByteCount
158         //
159         // Note that this could also change the contents of this.encoder, which is the same
160         // object that the caller is using, so the caller could mess up the encoder for us
161         // if they aren't careful.
162         internal unsafe virtual bool InternalFallback(char ch, ref char* chars)
163         {
164             // Shouldn't have null charStart
165             Debug.Assert(charStart != null,
166                 "[EncoderFallback.InternalFallbackBuffer]Fallback buffer is not initialized");
167
168             // Get our index, remember chars was preincremented to point at next char, so have to -1
169             int index = (int)(chars - charStart) - 1;
170
171             // See if it was a high surrogate
172             if (Char.IsHighSurrogate(ch))
173             {
174                 // See if there's a low surrogate to go with it
175                 if (chars >= this.charEnd)
176                 {
177                     // Nothing left in input buffer
178                     // No input, return 0 if mustflush is false
179                     if (this.encoder != null && !this.encoder.MustFlush)
180                     {
181                         // Done, nothing to fallback
182                         if (this.setEncoder)
183                         {
184                             bUsedEncoder = true;
185                             this.encoder.charLeftOver = ch;
186                         }
187                         bFallingBack = false;
188                         return false;
189                     }
190                 }
191                 else
192                 {
193                     // Might have a low surrogate
194                     char cNext = *chars;
195                     if (Char.IsLowSurrogate(cNext))
196                     {
197                         // If already falling back then fail
198                         if (bFallingBack && iRecursionCount++ > iMaxRecursion)
199                             ThrowLastCharRecursive(Char.ConvertToUtf32(ch, cNext));
200
201                         // Next is a surrogate, add it as surrogate pair, and increment chars
202                         chars++;
203                         bFallingBack = Fallback(ch, cNext, index);
204                         return bFallingBack;
205                     }
206
207                     // Next isn't a low surrogate, just fallback the high surrogate
208                 }
209             }
210
211             // If already falling back then fail
212             if (bFallingBack && iRecursionCount++ > iMaxRecursion)
213                 ThrowLastCharRecursive((int)ch);
214
215             // Fall back our char
216             bFallingBack = Fallback(ch, index);
217
218             return bFallingBack;
219         }
220
221         // private helper methods
222         internal void ThrowLastCharRecursive(int charRecursive)
223         {
224             // Throw it, using our complete character
225             throw new ArgumentException(
226                 SR.Format(SR.Argument_RecursiveFallback, charRecursive), "chars");
227         }
228     }
229 }