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