| File: PagedByteBuffer.cs | Web Access |
| Project: src\aspnetcore\src\Http\WebUtilities\src\Microsoft.AspNetCore.WebUtilities.csproj (Microsoft.AspNetCore.WebUtilities) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.IO.Pipelines; namespace Microsoft.AspNetCore.WebUtilities; internal sealed class PagedByteBuffer : IDisposable { internal const int PageSize = 1024; private readonly ArrayPool<byte> _arrayPool; private byte[]? _currentPage; private int _currentPageIndex; public PagedByteBuffer(ArrayPool<byte> arrayPool) { _arrayPool = arrayPool; Pages = new List<byte[]>(); } public int Length { get; private set; } internal bool Disposed { get; private set; } internal List<byte[]> Pages { get; } private byte[] CurrentPage { get { if (_currentPage == null || _currentPageIndex == _currentPage.Length) { _currentPage = _arrayPool.Rent(PageSize); Pages.Add(_currentPage); _currentPageIndex = 0; } return _currentPage; } } public void Add(byte[] buffer, int offset, int count) => Add(buffer.AsMemory(offset, count)); public void Add(ReadOnlyMemory<byte> memory) { ThrowIfDisposed(); while (!memory.IsEmpty) { var currentPage = CurrentPage; var copyLength = Math.Min(memory.Length, currentPage.Length - _currentPageIndex); memory.Slice(0, copyLength).CopyTo(currentPage.AsMemory(_currentPageIndex, copyLength)); Length += copyLength; _currentPageIndex += copyLength; memory = memory.Slice(copyLength); } } public void MoveTo(Stream stream) { ThrowIfDisposed(); for (var i = 0; i < Pages.Count; i++) { var page = Pages[i]; var length = (i == Pages.Count - 1) ? _currentPageIndex : page.Length; stream.Write(page, 0, length); } ClearBuffers(); } public async Task MoveToAsync(PipeWriter writer, CancellationToken cancellationToken) { ThrowIfDisposed(); for (var i = 0; i < Pages.Count; i++) { var page = Pages[i]; var length = (i == Pages.Count - 1) ? _currentPageIndex : page.Length; await writer.WriteAsync(page.AsMemory(0, length), cancellationToken); } ClearBuffers(); } public async Task MoveToAsync(Stream stream, CancellationToken cancellationToken) { ThrowIfDisposed(); for (var i = 0; i < Pages.Count; i++) { var page = Pages[i]; var length = (i == Pages.Count - 1) ? _currentPageIndex : page.Length; await stream.WriteAsync(page.AsMemory(0, length), cancellationToken); } ClearBuffers(); } public void Dispose() { if (!Disposed) { Disposed = true; ClearBuffers(); } } private void ClearBuffers() { for (var i = 0; i < Pages.Count; i++) { _arrayPool.Return(Pages[i]); } Pages.Clear(); _currentPage = null; Length = 0; _currentPageIndex = 0; } private void ThrowIfDisposed() { ObjectDisposedException.ThrowIf(Disposed, this); } }