You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.8 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.IO;
  27. #if HAVE_ASYNC
  28. using System.Threading;
  29. using System.Threading.Tasks;
  30. #endif
  31. namespace Newtonsoft.Json.Utilities
  32. {
  33. internal class Base64Encoder
  34. {
  35. private const int Base64LineSize = 76;
  36. private const int LineSizeInBytes = 57;
  37. private readonly char[] _charsLine = new char[Base64LineSize];
  38. private readonly TextWriter _writer;
  39. private byte[] _leftOverBytes;
  40. private int _leftOverBytesCount;
  41. public Base64Encoder(TextWriter writer)
  42. {
  43. ValidationUtils.ArgumentNotNull(writer, nameof(writer));
  44. _writer = writer;
  45. }
  46. private void ValidateEncode(byte[] buffer, int index, int count)
  47. {
  48. if (buffer == null)
  49. {
  50. throw new ArgumentNullException(nameof(buffer));
  51. }
  52. if (index < 0)
  53. {
  54. throw new ArgumentOutOfRangeException(nameof(index));
  55. }
  56. if (count < 0)
  57. {
  58. throw new ArgumentOutOfRangeException(nameof(count));
  59. }
  60. if (count > (buffer.Length - index))
  61. {
  62. throw new ArgumentOutOfRangeException(nameof(count));
  63. }
  64. }
  65. public void Encode(byte[] buffer, int index, int count)
  66. {
  67. ValidateEncode(buffer, index, count);
  68. if (_leftOverBytesCount > 0)
  69. {
  70. if(FulfillFromLeftover(buffer, index, ref count))
  71. {
  72. return;
  73. }
  74. int num2 = Convert.ToBase64CharArray(_leftOverBytes, 0, 3, _charsLine, 0);
  75. WriteChars(_charsLine, 0, num2);
  76. }
  77. StoreLeftOverBytes(buffer, index, ref count);
  78. int num4 = index + count;
  79. int length = LineSizeInBytes;
  80. while (index < num4)
  81. {
  82. if ((index + length) > num4)
  83. {
  84. length = num4 - index;
  85. }
  86. int num6 = Convert.ToBase64CharArray(buffer, index, length, _charsLine, 0);
  87. WriteChars(_charsLine, 0, num6);
  88. index += length;
  89. }
  90. }
  91. private void StoreLeftOverBytes(byte[] buffer, int index, ref int count)
  92. {
  93. int leftOverBytesCount = count % 3;
  94. if (leftOverBytesCount > 0)
  95. {
  96. count -= leftOverBytesCount;
  97. if (_leftOverBytes == null)
  98. {
  99. _leftOverBytes = new byte[3];
  100. }
  101. for (int i = 0; i < leftOverBytesCount; i++)
  102. {
  103. _leftOverBytes[i] = buffer[index + count + i];
  104. }
  105. }
  106. _leftOverBytesCount = leftOverBytesCount;
  107. }
  108. private bool FulfillFromLeftover(byte[] buffer, int index, ref int count)
  109. {
  110. int leftOverBytesCount = _leftOverBytesCount;
  111. while (leftOverBytesCount < 3 && count > 0)
  112. {
  113. _leftOverBytes[leftOverBytesCount++] = buffer[index++];
  114. count--;
  115. }
  116. if (count == 0 && leftOverBytesCount < 3)
  117. {
  118. _leftOverBytesCount = leftOverBytesCount;
  119. return true;
  120. }
  121. return false;
  122. }
  123. public void Flush()
  124. {
  125. if (_leftOverBytesCount > 0)
  126. {
  127. int count = Convert.ToBase64CharArray(_leftOverBytes, 0, _leftOverBytesCount, _charsLine, 0);
  128. WriteChars(_charsLine, 0, count);
  129. _leftOverBytesCount = 0;
  130. }
  131. }
  132. private void WriteChars(char[] chars, int index, int count)
  133. {
  134. _writer.Write(chars, index, count);
  135. }
  136. #if HAVE_ASYNC
  137. public async Task EncodeAsync(byte[] buffer, int index, int count, CancellationToken cancellationToken)
  138. {
  139. ValidateEncode(buffer, index, count);
  140. if (_leftOverBytesCount > 0)
  141. {
  142. if (FulfillFromLeftover(buffer, index, ref count))
  143. {
  144. return;
  145. }
  146. int num2 = Convert.ToBase64CharArray(_leftOverBytes, 0, 3, _charsLine, 0);
  147. await WriteCharsAsync(_charsLine, 0, num2, cancellationToken).ConfigureAwait(false);
  148. }
  149. StoreLeftOverBytes(buffer, index, ref count);
  150. int num4 = index + count;
  151. int length = LineSizeInBytes;
  152. while (index < num4)
  153. {
  154. if (index + length > num4)
  155. {
  156. length = num4 - index;
  157. }
  158. int num6 = Convert.ToBase64CharArray(buffer, index, length, _charsLine, 0);
  159. await WriteCharsAsync(_charsLine, 0, num6, cancellationToken).ConfigureAwait(false);
  160. index += length;
  161. }
  162. }
  163. private Task WriteCharsAsync(char[] chars, int index, int count, CancellationToken cancellationToken)
  164. {
  165. return _writer.WriteAsync(chars, index, count, cancellationToken);
  166. }
  167. public Task FlushAsync(CancellationToken cancellationToken)
  168. {
  169. if (cancellationToken.IsCancellationRequested)
  170. {
  171. return cancellationToken.FromCanceled();
  172. }
  173. if (_leftOverBytesCount > 0)
  174. {
  175. int count = Convert.ToBase64CharArray(_leftOverBytes, 0, _leftOverBytesCount, _charsLine, 0);
  176. _leftOverBytesCount = 0;
  177. return WriteCharsAsync(_charsLine, 0, count, cancellationToken);
  178. }
  179. return AsyncUtils.CompletedTask;
  180. }
  181. #endif
  182. }
  183. }