DecoderReplacementFallback.cs 6.4 KB

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