| File: Buffers\PagedBufferedTextWriter.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures) |
// 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.Diagnostics; using System.Runtime.CompilerServices; using System.Text; namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers; internal sealed class PagedBufferedTextWriter : TextWriter { private readonly TextWriter _inner; private readonly PagedCharBuffer _charBuffer; public PagedBufferedTextWriter(ArrayPool<char> pool, TextWriter inner) { _charBuffer = new PagedCharBuffer(new ArrayPoolBufferSource(pool)); _inner = inner; } public override Encoding Encoding => _inner.Encoding; public override void Flush() { // Don't do anything. We'll call FlushAsync. } public override Task FlushAsync() => FlushAsyncCore(); // private non-virtual for internal calling. // It first does a fast check to see if async is necessary, we inline this check. [MethodImpl(MethodImplOptions.AggressiveInlining)] private Task FlushAsyncCore() { var length = _charBuffer.Length; if (length == 0) { // If nothing sync buffered return CompletedTask, // so we can fast-path skip async state-machine creation return Task.CompletedTask; } return FlushAsyncAwaited(); } private async Task FlushAsyncAwaited() { var length = _charBuffer.Length; Debug.Assert(length > 0); var pages = _charBuffer.Pages; var count = pages.Count; for (var i = 0; i < count; i++) { var page = pages[i]; var pageLength = Math.Min(length, page.Length); if (pageLength != 0) { await _inner.WriteAsync(page, index: 0, count: pageLength); } length -= pageLength; } Debug.Assert(length == 0); _charBuffer.Clear(); } public override void Write(char value) { _charBuffer.Append(value); } public override void Write(char[] buffer) { if (buffer == null) { return; } _charBuffer.Append(buffer, 0, buffer.Length); } public override void Write(char[] buffer, int index, int count) { ArgumentNullException.ThrowIfNull(buffer); _charBuffer.Append(buffer, index, count); } public override void Write(string value) { if (value == null) { return; } _charBuffer.Append(value); } public override Task WriteAsync(char value) { var flushTask = FlushAsyncCore(); return flushTask.IsCompletedSuccessfully ? _inner.WriteAsync(value) : WriteAsyncAwaited(flushTask, value); } private async Task WriteAsyncAwaited(Task flushTask, char value) { await flushTask; await _inner.WriteAsync(value); } public override Task WriteAsync(char[] buffer, int index, int count) { var flushTask = FlushAsyncCore(); return flushTask.IsCompletedSuccessfully ? _inner.WriteAsync(buffer, index, count) : WriteAsyncAwaited(flushTask, buffer, index, count); } private async Task WriteAsyncAwaited(Task flushTask, char[] buffer, int index, int count) { await flushTask; await _inner.WriteAsync(buffer, index, count); } public override Task WriteAsync(string value) { var flushTask = FlushAsyncCore(); return flushTask.IsCompletedSuccessfully ? _inner.WriteAsync(value) : WriteAsyncAwaited(flushTask, value); } private async Task WriteAsyncAwaited(Task flushTask, string value) { await flushTask; await _inner.WriteAsync(value); } protected override void Dispose(bool disposing) { base.Dispose(disposing); _charBuffer.Dispose(); } }