PooledMemoryStream.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using System;
  2. using System.Buffers;
  3. using System.IO;
  4. using System.Runtime.CompilerServices;
  5. namespace Masuit.Tools.Systems;
  6. /// <summary>
  7. /// 池化内存流
  8. /// </summary>
  9. public sealed class PooledMemoryStream : Stream
  10. {
  11. /// <summary>
  12. /// 终结器
  13. /// </summary>
  14. ~PooledMemoryStream()
  15. {
  16. Dispose(true);
  17. }
  18. private const float OverExpansionFactor = 2;
  19. private byte[] _data = Array.Empty<byte>();
  20. private int _length;
  21. private readonly ArrayPool<byte> _pool;
  22. private bool _isDisposed;
  23. public PooledMemoryStream() : this(ArrayPool<byte>.Shared)
  24. {
  25. }
  26. public PooledMemoryStream(byte[] buffer) : this(ArrayPool<byte>.Shared, buffer.Length)
  27. {
  28. Array.Copy(buffer, 0, _data, 0, buffer.Length);
  29. }
  30. public PooledMemoryStream(ArrayPool<byte> arrayPool, int capacity = 0)
  31. {
  32. _pool = arrayPool ?? throw new ArgumentNullException(nameof(arrayPool));
  33. if (capacity > 0)
  34. {
  35. _data = _pool.Rent(capacity);
  36. }
  37. }
  38. public override bool CanRead => true;
  39. public override bool CanSeek => true;
  40. public override bool CanWrite => true;
  41. public override long Length => _length;
  42. public override long Position { get; set; }
  43. public long Capacity => _data?.Length ?? 0;
  44. #if NETCOREAPP || NET452
  45. public Span<byte> GetSpan()
  46. {
  47. return _data.AsSpan(0, _length);
  48. }
  49. public Memory<byte> GetMemory()
  50. {
  51. return _data.AsMemory(0, _length);
  52. }
  53. public ArraySegment<byte> ToArraySegment()
  54. {
  55. return new ArraySegment<byte>(_data, 0, (int)Length);
  56. }
  57. #endif
  58. public override void Flush()
  59. {
  60. AssertNotDisposed();
  61. }
  62. public override int Read(byte[] buffer, int offset, int count)
  63. {
  64. AssertNotDisposed();
  65. if (count == 0)
  66. {
  67. return 0;
  68. }
  69. var available = Math.Min(count, Length - Position);
  70. Array.Copy(_data, Position, buffer, offset, available);
  71. Position += available;
  72. return (int)available;
  73. }
  74. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  75. public override long Seek(long offset, SeekOrigin origin)
  76. {
  77. AssertNotDisposed();
  78. switch (origin)
  79. {
  80. case SeekOrigin.Current:
  81. if (Position + offset < 0 || Position + offset > Capacity)
  82. {
  83. throw new ArgumentOutOfRangeException(nameof(offset));
  84. }
  85. Position += offset;
  86. _length = (int)Math.Max(Position, _length);
  87. return Position;
  88. case SeekOrigin.Begin:
  89. if (offset < 0 || offset > Capacity)
  90. {
  91. throw new ArgumentOutOfRangeException(nameof(offset));
  92. }
  93. Position = offset;
  94. _length = (int)Math.Max(Position, _length);
  95. return Position;
  96. case SeekOrigin.End:
  97. if (Length + offset < 0)
  98. {
  99. throw new ArgumentOutOfRangeException(nameof(offset));
  100. }
  101. if (Length + offset > Capacity)
  102. {
  103. SetCapacity((int)(Length + offset));
  104. }
  105. Position = Length + offset;
  106. _length = (int)Math.Max(Position, _length);
  107. return Position;
  108. default:
  109. throw new ArgumentOutOfRangeException(nameof(origin));
  110. }
  111. }
  112. public override void SetLength(long value)
  113. {
  114. AssertNotDisposed();
  115. if (value < 0)
  116. {
  117. throw new ArgumentOutOfRangeException(nameof(value));
  118. }
  119. if (value > Capacity)
  120. {
  121. SetCapacity((int)value);
  122. }
  123. _length = (int)value;
  124. if (Position > Length)
  125. {
  126. Position = Length;
  127. }
  128. }
  129. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  130. public override void Write(byte[] buffer, int offset, int count)
  131. {
  132. AssertNotDisposed();
  133. if (count == 0)
  134. {
  135. return;
  136. }
  137. if (Capacity - Position < count)
  138. {
  139. SetCapacity((int)(OverExpansionFactor * (Position + count)));
  140. }
  141. Buffer.BlockCopy(buffer, offset, _data, (int)Position, count);
  142. Position += count;
  143. _length = (int)Math.Max(Position, _length);
  144. }
  145. public void WriteTo(Stream stream)
  146. {
  147. if (stream == null)
  148. {
  149. throw new ArgumentNullException(nameof(stream));
  150. }
  151. AssertNotDisposed();
  152. stream.Write(_data, 0, (int)Length);
  153. }
  154. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  155. protected override void Dispose(bool disposing)
  156. {
  157. if (disposing)
  158. {
  159. _isDisposed = true;
  160. Position = 0;
  161. _length = 0;
  162. if (_data != null)
  163. {
  164. _pool.Return(_data);
  165. _data = null;
  166. }
  167. }
  168. base.Dispose(disposing);
  169. }
  170. private void SetCapacity(int newCapacity)
  171. {
  172. var newData = _pool.Rent(newCapacity);
  173. if (_data != null)
  174. {
  175. Buffer.BlockCopy(_data, 0, newData, 0, (int)Position);
  176. _pool.Return(_data);
  177. }
  178. _data = newData;
  179. }
  180. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  181. private void AssertNotDisposed()
  182. {
  183. if (_isDisposed)
  184. {
  185. throw new ObjectDisposedException(nameof(PooledMemoryStream));
  186. }
  187. }
  188. }