PooledMemoryStream.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. using System;
  2. using System.Buffers;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Runtime.CompilerServices;
  7. namespace Masuit.Tools.Systems;
  8. /// <summary>
  9. /// 池化内存流
  10. /// </summary>
  11. public sealed class PooledMemoryStream : Stream, IEnumerable<byte>
  12. {
  13. /// <summary>
  14. /// 终结器
  15. /// </summary>
  16. ~PooledMemoryStream()
  17. {
  18. Dispose(true);
  19. }
  20. private const float OverExpansionFactor = 2;
  21. private byte[] _data = new byte[0];
  22. private int _length;
  23. private readonly ArrayPool<byte> _pool;
  24. private bool _isDisposed;
  25. public PooledMemoryStream() : this(ArrayPool<byte>.Shared)
  26. {
  27. }
  28. public PooledMemoryStream(byte[] buffer) : this(ArrayPool<byte>.Shared, buffer.Length)
  29. {
  30. Buffer.BlockCopy(buffer, 0, _data, 0, buffer.Length);
  31. }
  32. public PooledMemoryStream(ArrayPool<byte> arrayPool, int capacity = 0)
  33. {
  34. _pool = arrayPool ?? throw new ArgumentNullException(nameof(arrayPool));
  35. if (capacity > 0)
  36. {
  37. _data = _pool.Rent(capacity);
  38. }
  39. }
  40. public override bool CanRead => true;
  41. public override bool CanSeek => true;
  42. public override bool CanWrite => true;
  43. public override long Length => _length;
  44. public override long Position { get; set; }
  45. public long Capacity => _data?.Length ?? 0;
  46. #if NETCOREAPP || NET452
  47. public Span<byte> GetSpan()
  48. {
  49. return _data.AsSpan(0, _length);
  50. }
  51. public Memory<byte> GetMemory()
  52. {
  53. return _data.AsMemory(0, _length);
  54. }
  55. public ArraySegment<byte> ToArraySegment()
  56. {
  57. return new ArraySegment<byte>(_data, 0, (int)Length);
  58. }
  59. #endif
  60. public override void Flush()
  61. {
  62. AssertNotDisposed();
  63. }
  64. /// <summary>
  65. /// 读取到字节数组
  66. /// </summary>
  67. /// <param name="buffer"></param>
  68. /// <param name="offset"></param>
  69. /// <param name="count"></param>
  70. /// <returns></returns>
  71. public override int Read(byte[] buffer, int offset, int count)
  72. {
  73. AssertNotDisposed();
  74. if (count == 0)
  75. {
  76. return 0;
  77. }
  78. var available = Math.Min(count, Length - Position);
  79. Array.Copy(_data, Position, buffer, offset, available);
  80. Position += available;
  81. return (int)available;
  82. }
  83. /// <summary>
  84. /// 改变游标位置
  85. /// </summary>
  86. /// <param name="offset"></param>
  87. /// <param name="origin"></param>
  88. /// <returns></returns>
  89. /// <exception cref="ArgumentOutOfRangeException"></exception>
  90. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  91. public override long Seek(long offset, SeekOrigin origin)
  92. {
  93. AssertNotDisposed();
  94. switch (origin)
  95. {
  96. case SeekOrigin.Current:
  97. if (Position + offset < 0 || Position + offset > Capacity)
  98. {
  99. throw new ArgumentOutOfRangeException(nameof(offset));
  100. }
  101. Position += offset;
  102. _length = (int)Math.Max(Position, _length);
  103. return Position;
  104. case SeekOrigin.Begin:
  105. if (offset < 0 || offset > Capacity)
  106. {
  107. throw new ArgumentOutOfRangeException(nameof(offset));
  108. }
  109. Position = offset;
  110. _length = (int)Math.Max(Position, _length);
  111. return Position;
  112. case SeekOrigin.End:
  113. if (Length + offset < 0)
  114. {
  115. throw new ArgumentOutOfRangeException(nameof(offset));
  116. }
  117. if (Length + offset > Capacity)
  118. {
  119. SetCapacity((int)(Length + offset));
  120. }
  121. Position = Length + offset;
  122. _length = (int)Math.Max(Position, _length);
  123. return Position;
  124. default:
  125. throw new ArgumentOutOfRangeException(nameof(origin));
  126. }
  127. }
  128. /// <summary>
  129. /// 设置内容长度
  130. /// </summary>
  131. /// <param name="value"></param>
  132. /// <exception cref="ArgumentOutOfRangeException"></exception>
  133. public override void SetLength(long value)
  134. {
  135. AssertNotDisposed();
  136. if (value < 0)
  137. {
  138. throw new ArgumentOutOfRangeException(nameof(value));
  139. }
  140. if (value > Capacity)
  141. {
  142. SetCapacity((int)value);
  143. }
  144. _length = (int)value;
  145. if (Position > Length)
  146. {
  147. Position = Length;
  148. }
  149. }
  150. /// <summary>
  151. /// 写入到字节数组
  152. /// </summary>
  153. /// <param name="buffer"></param>
  154. /// <param name="offset"></param>
  155. /// <param name="count"></param>
  156. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  157. public override void Write(byte[] buffer, int offset, int count)
  158. {
  159. AssertNotDisposed();
  160. if (count == 0)
  161. {
  162. return;
  163. }
  164. if (Capacity - Position < count)
  165. {
  166. SetCapacity((int)(OverExpansionFactor * (Position + count)));
  167. }
  168. Array.Copy(buffer, offset, _data, Position, count);
  169. Position += count;
  170. _length = (int)Math.Max(Position, _length);
  171. }
  172. /// <summary>
  173. /// 写入到另一个流
  174. /// </summary>
  175. /// <param name="stream"></param>
  176. /// <exception cref="ArgumentNullException"></exception>
  177. public void WriteTo(Stream stream)
  178. {
  179. if (stream == null)
  180. {
  181. throw new ArgumentNullException(nameof(stream));
  182. }
  183. AssertNotDisposed();
  184. stream.Write(_data, 0, (int)Length);
  185. }
  186. /// <summary>
  187. /// 获取流的字节数组
  188. /// </summary>
  189. /// <returns></returns>
  190. public byte[] GetBuffer()
  191. {
  192. AssertNotDisposed();
  193. if (_data.Length == Length)
  194. {
  195. return _data;
  196. }
  197. var buffer = new byte[Length];
  198. Buffer.BlockCopy(_data, 0, buffer, 0, buffer.Length);
  199. return buffer;
  200. }
  201. /// <summary>
  202. /// 获取流的字节数组
  203. /// </summary>
  204. /// <returns></returns>
  205. public byte[] ToArray()
  206. {
  207. return GetBuffer();
  208. }
  209. /// <summary>
  210. ///
  211. /// </summary>
  212. /// <param name="disposing"></param>
  213. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  214. protected override void Dispose(bool disposing)
  215. {
  216. if (disposing)
  217. {
  218. _isDisposed = true;
  219. Position = 0;
  220. _length = 0;
  221. if (_data != null)
  222. {
  223. _pool.Return(_data);
  224. _data = null;
  225. }
  226. }
  227. base.Dispose(disposing);
  228. }
  229. private void SetCapacity(int newCapacity)
  230. {
  231. var newData = _pool.Rent(newCapacity);
  232. if (_data != null)
  233. {
  234. Array.Copy(_data, 0, newData, 0, Position);
  235. _pool.Return(_data);
  236. }
  237. _data = newData;
  238. }
  239. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  240. private void AssertNotDisposed()
  241. {
  242. if (_isDisposed)
  243. {
  244. throw new ObjectDisposedException(nameof(PooledMemoryStream));
  245. }
  246. }
  247. /// <summary>Returns an enumerator that iterates through a collection.</summary>
  248. /// <returns>An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.</returns>
  249. IEnumerator IEnumerable.GetEnumerator()
  250. {
  251. return GetEnumerator();
  252. }
  253. /// <summary>
  254. ///
  255. /// </summary>
  256. /// <returns></returns>
  257. public IEnumerator<byte> GetEnumerator()
  258. {
  259. for (var i = 0; i < Length; i++)
  260. {
  261. yield return _data[i];
  262. }
  263. }
  264. }