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