123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- using System;
- using System.Buffers;
- using System.IO;
- using System.Runtime.CompilerServices;
- namespace Masuit.Tools.Systems;
- /// <summary>
- /// 大型内存流,最大可支持1TB数据,推荐当数据流大于2GB时使用
- /// </summary>
- public class LargeMemoryStream : Stream
- {
- /// <summary>
- /// 终结器
- /// </summary>
- ~LargeMemoryStream()
- {
- Dispose(true);
- }
- private const int PageSize = 1024000000;
- private const int AllocStep = 1024;
- private byte[][] _streamBuffers;
- private int _pageCount;
- private long _allocatedBytes;
- private long _position;
- private long _length;
- private bool _isDisposed;
- private int GetPageCount(long length)
- {
- int pageCount = (int)(length / PageSize) + 1;
- if (length % PageSize == 0)
- {
- pageCount--;
- }
- return pageCount;
- }
- private void ExtendPages()
- {
- if (_streamBuffers == null)
- {
- _streamBuffers = new byte[AllocStep][];
- }
- else
- {
- var streamBuffers = new byte[_streamBuffers.Length + AllocStep][];
- Buffer.BlockCopy(_streamBuffers, 0, streamBuffers, 0, _streamBuffers.Length);
- _streamBuffers = streamBuffers;
- }
- _pageCount = _streamBuffers.Length;
- }
- private void AllocSpaceIfNeeded(long value)
- {
- switch (value)
- {
- case < 0:
- throw new InvalidOperationException("AllocSpaceIfNeeded < 0");
- case 0:
- return;
- }
- int currentPageCount = GetPageCount(_allocatedBytes);
- int neededPageCount = GetPageCount(value);
- while (currentPageCount < neededPageCount)
- {
- if (currentPageCount == _pageCount)
- ExtendPages();
- _streamBuffers[currentPageCount++] = ArrayPool<byte>.Shared.Rent(PageSize);
- }
- _allocatedBytes = (long)currentPageCount * PageSize;
- value = Math.Max(value, _length);
- if (_position > (_length = value))
- {
- _position = _length;
- }
- }
- public override bool CanRead => true;
- public override bool CanSeek => true;
- public override bool CanWrite => true;
- public override long Length => _length;
- public override long Position
- {
- get => _position;
- set
- {
- if (value > _length)
- {
- throw new InvalidOperationException("Position > Length");
- }
- if (value < 0)
- {
- throw new InvalidOperationException("Position < 0");
- }
- _position = value;
- }
- }
- #if NETCOREAPP || NET452
- public Span<byte[]> GetSpan()
- {
- return _streamBuffers.AsSpan(0, _streamBuffers.Length);
- }
- public Memory<byte[]> GetMemory()
- {
- return _streamBuffers.AsMemory(0, _streamBuffers.Length);
- }
- public ArraySegment<byte[]> ToArraySegment()
- {
- return new ArraySegment<byte[]>(_streamBuffers, 0, _streamBuffers.Length);
- }
- #endif
- public override void Flush()
- {
- AssertNotDisposed();
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- AssertNotDisposed();
- int currentPage = (int)(_position / PageSize);
- int currentOffset = (int)(_position % PageSize);
- int currentLength = PageSize - currentOffset;
- long startPosition = _position;
- if (startPosition + count > _length)
- {
- count = (int)(_length - startPosition);
- }
- while (count != 0 && _position < _length)
- {
- if (currentLength > count)
- {
- currentLength = count;
- }
- Buffer.BlockCopy(_streamBuffers[currentPage++], currentOffset, buffer, offset, currentLength);
- offset += currentLength;
- _position += currentLength;
- count -= currentLength;
- currentOffset = 0;
- currentLength = PageSize;
- }
- return (int)(_position - startPosition);
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- AssertNotDisposed();
- switch (origin)
- {
- case SeekOrigin.Begin:
- break;
- case SeekOrigin.Current:
- offset += _position;
- break;
- case SeekOrigin.End:
- offset = _length - offset;
- break;
- default:
- throw new ArgumentOutOfRangeException("origin");
- }
- return Position = offset;
- }
- public override void SetLength(long value)
- {
- switch (value)
- {
- case < 0:
- throw new InvalidOperationException("SetLength < 0");
- case 0:
- _streamBuffers = null;
- _allocatedBytes = _position = _length = 0;
- _pageCount = 0;
- return;
- }
- int currentPageCount = GetPageCount(_allocatedBytes);
- int neededPageCount = GetPageCount(value);
- while (currentPageCount > neededPageCount)
- {
- ArrayPool<byte>.Shared.Return(_streamBuffers[--currentPageCount], true);
- _streamBuffers[currentPageCount] = null;
- }
- AllocSpaceIfNeeded(value);
- if (_position > (_length = value))
- {
- _position = _length;
- }
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- AssertNotDisposed();
- int currentPage = (int)(_position / PageSize);
- int currentOffset = (int)(_position % PageSize);
- int currentLength = PageSize - currentOffset;
- AllocSpaceIfNeeded(_position + count);
- while (count != 0)
- {
- if (currentLength > count)
- {
- currentLength = count;
- }
- Buffer.BlockCopy(buffer, offset, _streamBuffers[currentPage++], currentOffset, currentLength);
- offset += currentLength;
- _position += currentLength;
- count -= currentLength;
- currentOffset = 0;
- currentLength = PageSize;
- }
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- _isDisposed = true;
- Position = 0;
- _length = 0;
- if (_streamBuffers != null)
- {
- foreach (var bytes in _streamBuffers)
- {
- if (bytes != null)
- {
- ArrayPool<byte>.Shared.Return(bytes);
- }
- }
- _streamBuffers = null;
- }
- }
- base.Dispose(disposing);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void AssertNotDisposed()
- {
- if (_isDisposed)
- {
- throw new ObjectDisposedException(nameof(PooledMemoryStream));
- }
- }
- }
|