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