a9ce9c10efd783af58e13e0a9cfb835def23d887
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Text / EncoderReplacementFallback.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.Runtime;
7 using System.Diagnostics;
8 using System.Diagnostics.Contracts;
9
10 namespace System.Text
11 {
12     public sealed class EncoderReplacementFallback : EncoderFallback
13     {
14         // Our variables
15         private String strDefault;
16
17         // Construction.  Default replacement fallback uses no best fit and ? replacement string
18         public EncoderReplacementFallback() : this("?")
19         {
20         }
21
22         public EncoderReplacementFallback(String replacement)
23         {
24             // Must not be null
25             if (replacement == null)
26                 throw new ArgumentNullException(nameof(replacement));
27             Contract.EndContractBlock();
28
29             // Make sure it doesn't have bad surrogate pairs
30             bool bFoundHigh = false;
31             for (int i = 0; i < replacement.Length; i++)
32             {
33                 // Found a surrogate?
34                 if (Char.IsSurrogate(replacement, i))
35                 {
36                     // High or Low?
37                     if (Char.IsHighSurrogate(replacement, i))
38                     {
39                         // if already had a high one, stop
40                         if (bFoundHigh)
41                             break;  // break & throw at the bFoundHIgh below
42                         bFoundHigh = true;
43                     }
44                     else
45                     {
46                         // Low, did we have a high?
47                         if (!bFoundHigh)
48                         {
49                             // Didn't have one, make if fail when we stop
50                             bFoundHigh = true;
51                             break;
52                         }
53
54                         // Clear flag
55                         bFoundHigh = false;
56                     }
57                 }
58                 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
59                 else if (bFoundHigh)
60                     break;
61             }
62             if (bFoundHigh)
63                 throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)));
64
65             strDefault = replacement;
66         }
67
68         public String DefaultString
69         {
70             get
71             {
72                 return strDefault;
73             }
74         }
75
76         public override EncoderFallbackBuffer CreateFallbackBuffer()
77         {
78             return new EncoderReplacementFallbackBuffer(this);
79         }
80
81         // Maximum number of characters that this instance of this fallback could return
82         public override int MaxCharCount
83         {
84             get
85             {
86                 return strDefault.Length;
87             }
88         }
89
90         public override bool Equals(Object value)
91         {
92             EncoderReplacementFallback that = value as EncoderReplacementFallback;
93             if (that != null)
94             {
95                 return (strDefault == that.strDefault);
96             }
97             return (false);
98         }
99
100         public override int GetHashCode()
101         {
102             return strDefault.GetHashCode();
103         }
104     }
105
106
107
108     public sealed class EncoderReplacementFallbackBuffer : EncoderFallbackBuffer
109     {
110         // Store our default string
111         private String strDefault;
112         private int fallbackCount = -1;
113         private int fallbackIndex = -1;
114
115         // Construction
116         public EncoderReplacementFallbackBuffer(EncoderReplacementFallback fallback)
117         {
118             // 2X in case we're a surrogate pair
119             strDefault = fallback.DefaultString + fallback.DefaultString;
120         }
121
122         // Fallback Methods
123         public override bool Fallback(char charUnknown, int index)
124         {
125             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
126             // character in our array.
127             if (fallbackCount >= 1)
128             {
129                 // If we're recursive we may still have something in our buffer that makes this a surrogate
130                 if (char.IsHighSurrogate(charUnknown) && fallbackCount >= 0 &&
131                     char.IsLowSurrogate(strDefault[fallbackIndex + 1]))
132                     ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknown, strDefault[fallbackIndex + 1]));
133
134                 // Nope, just one character
135                 ThrowLastCharRecursive(unchecked((int)charUnknown));
136             }
137
138             // Go ahead and get our fallback
139             // Divide by 2 because we aren't a surrogate pair
140             fallbackCount = strDefault.Length / 2;
141             fallbackIndex = -1;
142
143             return fallbackCount != 0;
144         }
145
146         public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
147         {
148             // Double check input surrogate pair
149             if (!Char.IsHighSurrogate(charUnknownHigh))
150                 throw new ArgumentOutOfRangeException(nameof(charUnknownHigh),
151                     SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF));
152
153             if (!Char.IsLowSurrogate(charUnknownLow))
154                 throw new ArgumentOutOfRangeException(nameof(charUnknownLow),
155                     SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF));
156             Contract.EndContractBlock();
157
158             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
159             // character in our array.
160             if (fallbackCount >= 1)
161                 ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknownHigh, charUnknownLow));
162
163             // Go ahead and get our fallback
164             fallbackCount = strDefault.Length;
165             fallbackIndex = -1;
166
167             return fallbackCount != 0;
168         }
169
170         public override char GetNextChar()
171         {
172             // We want it to get < 0 because == 0 means that the current/last character is a fallback
173             // and we need to detect recursion.  We could have a flag but we already have this counter.
174             fallbackCount--;
175             fallbackIndex++;
176
177             // Do we have anything left? 0 is now last fallback char, negative is nothing left
178             if (fallbackCount < 0)
179                 return '\0';
180
181             // Need to get it out of the buffer.
182             // Make sure it didn't wrap from the fast count-- path
183             if (fallbackCount == int.MaxValue)
184             {
185                 fallbackCount = -1;
186                 return '\0';
187             }
188
189             // Now make sure its in the expected range
190             Debug.Assert(fallbackIndex < strDefault.Length && fallbackIndex >= 0,
191                             "Index exceeds buffer range");
192
193             return strDefault[fallbackIndex];
194         }
195
196         public override bool MovePrevious()
197         {
198             // Back up one, only if we just processed the last character (or earlier)
199             if (fallbackCount >= -1 && fallbackIndex >= 0)
200             {
201                 fallbackIndex--;
202                 fallbackCount++;
203                 return true;
204             }
205
206             // Return false 'cause we couldn't do it.
207             return false;
208         }
209
210         // How many characters left to output?
211         public override int Remaining
212         {
213             get
214             {
215                 // Our count is 0 for 1 character left.
216                 return (fallbackCount < 0) ? 0 : fallbackCount;
217             }
218         }
219
220         // Clear the buffer
221         public override unsafe void Reset()
222         {
223             fallbackCount = -1;
224             fallbackIndex = 0;
225             charStart = null;
226             bFallingBack = false;
227         }
228     }
229 }
230