File: Rendering\Buffering\BufferedTextWriter.cs
Web Access
Project: src\src\Components\Endpoints\src\Microsoft.AspNetCore.Components.Endpoints.csproj (Microsoft.AspNetCore.Components.Endpoints)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text;
 
namespace Microsoft.AspNetCore.Components.Endpoints.Rendering;
 
internal class BufferedTextWriter : TextWriter
{
    private const int PageSize = 256;
    private readonly TextWriter _underlying;
    private readonly StringBuilder _charArraySegmentBuilder = new();
    private TextChunkListBuilder _currentOutput;
    private TextChunkListBuilder? _previousOutput;
    private Task _currentFlushAsyncTask = Task.CompletedTask;
 
    public BufferedTextWriter(TextWriter underlying)
    {
        _underlying = underlying;
        _currentOutput = new(PageSize);
    }
 
    public override Encoding Encoding => Encoding.UTF8;
 
    public override void Write(char value)
        => _currentOutput.Add(new TextChunk(value));
 
    public override void Write(char[] buffer, int index, int count)
        => _currentOutput.Add(new TextChunk(new ArraySegment<char>(buffer, index, count), _charArraySegmentBuilder));
 
    public override void Write(string? value)
    {
        if (value is not null)
        {
            _currentOutput.Add(new TextChunk(value));
        }
    }
 
    public override void Write(int value)
        => _currentOutput.Add(new TextChunk(value));
 
    public override void Flush()
        => throw new NotSupportedException();
 
    public override Task FlushAsync()
    {
        _currentFlushAsyncTask = FlushAsyncCore(_currentFlushAsyncTask);
        return _currentFlushAsyncTask;
    }
 
    private async Task FlushAsyncCore(Task priorTask)
    {
        // Must always wait for prior flushes to complete first, since they are
        // using _previousOutput and nothing else is allowed to do so
        if (!priorTask.IsCompletedSuccessfully)
        {
            await priorTask;
        }
 
        // Swap buffers
        var outputToFlush = _currentOutput;
        _currentOutput = _previousOutput ?? new(PageSize);
        _previousOutput = outputToFlush;
 
        await outputToFlush.WriteToAsync(_underlying, _charArraySegmentBuilder.ToString());
        outputToFlush.Clear();
        await _underlying.FlushAsync();
    }
}