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.
7 using System
.Diagnostics
;
11 public sealed class EncoderReplacementFallback
: EncoderFallback
14 private readonly string _strDefault
;
16 // Construction. Default replacement fallback uses no best fit and ? replacement string
17 public EncoderReplacementFallback() : this("?")
21 public EncoderReplacementFallback(string replacement
)
24 if (replacement
== null)
25 throw new ArgumentNullException(nameof(replacement
));
27 // Make sure it doesn't have bad surrogate pairs
28 bool bFoundHigh
= false;
29 for (int i
= 0; i
< replacement
.Length
; i
++)
32 if (char.IsSurrogate(replacement
, i
))
35 if (char.IsHighSurrogate(replacement
, i
))
37 // if already had a high one, stop
39 break; // break & throw at the bFoundHIgh below
44 // Low, did we have a high?
47 // Didn't have one, make if fail when we stop
56 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
61 throw new ArgumentException(SR
.Format(SR
.Argument_InvalidCharSequenceNoIndex
, nameof(replacement
)));
63 _strDefault
= replacement
;
66 public string DefaultString
=> _strDefault
;
68 public override EncoderFallbackBuffer
CreateFallbackBuffer() =>
69 new EncoderReplacementFallbackBuffer(this);
71 // Maximum number of characters that this instance of this fallback could return
72 public override int MaxCharCount
=> _strDefault
.Length
;
74 public override bool Equals(object? value) =>
75 value is EncoderReplacementFallback that
&&
76 _strDefault
== that
._strDefault
;
78 public override int GetHashCode() => _strDefault
.GetHashCode();
81 public sealed class EncoderReplacementFallbackBuffer
: EncoderFallbackBuffer
83 // Store our default string
84 private readonly string _strDefault
;
85 private int _fallbackCount
= -1;
86 private int _fallbackIndex
= -1;
89 public EncoderReplacementFallbackBuffer(EncoderReplacementFallback fallback
)
91 // 2X in case we're a surrogate pair
92 _strDefault
= fallback
.DefaultString
+ fallback
.DefaultString
;
96 public override bool Fallback(char charUnknown
, int index
)
98 // If we had a buffer already we're being recursive, throw, it's probably at the suspect
99 // character in our array.
100 if (_fallbackCount
>= 1)
102 // If we're recursive we may still have something in our buffer that makes this a surrogate
103 if (char.IsHighSurrogate(charUnknown
) && _fallbackCount
>= 0 &&
104 char.IsLowSurrogate(_strDefault
[_fallbackIndex
+ 1]))
105 ThrowLastCharRecursive(char.ConvertToUtf32(charUnknown
, _strDefault
[_fallbackIndex
+ 1]));
107 // Nope, just one character
108 ThrowLastCharRecursive(unchecked((int)charUnknown
));
111 // Go ahead and get our fallback
112 // Divide by 2 because we aren't a surrogate pair
113 _fallbackCount
= _strDefault
.Length
/ 2;
116 return _fallbackCount
!= 0;
119 public override bool Fallback(char charUnknownHigh
, char charUnknownLow
, int index
)
121 // Double check input surrogate pair
122 if (!char.IsHighSurrogate(charUnknownHigh
))
123 throw new ArgumentOutOfRangeException(nameof(charUnknownHigh
),
124 SR
.Format(SR
.ArgumentOutOfRange_Range
, 0xD800, 0xDBFF));
126 if (!char.IsLowSurrogate(charUnknownLow
))
127 throw new ArgumentOutOfRangeException(nameof(charUnknownLow
),
128 SR
.Format(SR
.ArgumentOutOfRange_Range
, 0xDC00, 0xDFFF));
130 // If we had a buffer already we're being recursive, throw, it's probably at the suspect
131 // character in our array.
132 if (_fallbackCount
>= 1)
133 ThrowLastCharRecursive(char.ConvertToUtf32(charUnknownHigh
, charUnknownLow
));
135 // Go ahead and get our fallback
136 _fallbackCount
= _strDefault
.Length
;
139 return _fallbackCount
!= 0;
142 public override char GetNextChar()
144 // We want it to get < 0 because == 0 means that the current/last character is a fallback
145 // and we need to detect recursion. We could have a flag but we already have this counter.
149 // Do we have anything left? 0 is now last fallback char, negative is nothing left
150 if (_fallbackCount
< 0)
153 // Need to get it out of the buffer.
154 // Make sure it didn't wrap from the fast count-- path
155 if (_fallbackCount
== int.MaxValue
)
161 // Now make sure its in the expected range
162 Debug
.Assert(_fallbackIndex
< _strDefault
.Length
&& _fallbackIndex
>= 0,
163 "Index exceeds buffer range");
165 return _strDefault
[_fallbackIndex
];
168 public override bool MovePrevious()
170 // Back up one, only if we just processed the last character (or earlier)
171 if (_fallbackCount
>= -1 && _fallbackIndex
>= 0)
178 // Return false 'cause we couldn't do it.
182 // How many characters left to output?
183 public override int Remaining
=>
184 // Our count is 0 for 1 character left.
185 (_fallbackCount
< 0) ? 0 : _fallbackCount
;
188 public override unsafe void Reset()
193 bFallingBack
= false;