EncoderReplacementFallback.cs 7.5 KB

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