64 lines
2.9 KiB
C#
64 lines
2.9 KiB
C#
using AyCode.Core.Serializers.Binaries;
|
|
using System;
|
|
using System.IO.Pipelines;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace AyCode.Core.Tests.Serialization;
|
|
|
|
/// <summary>
|
|
/// Test/benchmark-only extension methods for populating <see cref="AsyncPipeReaderInput"/>
|
|
/// from <see cref="System.IO.Pipelines.PipeReader"/>-backed transports (NamedPipe, FileStream,
|
|
/// custom pipe sources).
|
|
///
|
|
/// <para><b>Why test-only:</b> in real production, the consuming application already has its own
|
|
/// reader-task that reads from the pipe and pushes bytes via <c>AsyncPipeReaderInput.Feed</c>
|
|
/// — providing this drain extension publicly would duplicate that responsibility and confuse
|
|
/// the canonical push-pattern. The extension is kept here for unit-test scaffolding and the
|
|
/// streaming benchmark; production NuGet consumers should write their own drain logic in their
|
|
/// own reader-task following the application's threading model.</para>
|
|
/// </summary>
|
|
public static class AsyncPipeReaderInputExtensions
|
|
{
|
|
/// <summary>
|
|
/// Drains a <see cref="PipeReader"/> end-to-end into the <see cref="AsyncPipeReaderInput"/>:
|
|
/// calls <see cref="AsyncPipeReaderInput.Feed"/> on each segment and
|
|
/// <see cref="AsyncPipeReaderInput.Complete"/> when the pipe completes.
|
|
///
|
|
/// <para>Typical usage (test-only): NamedPipe IPC and FileStream-via-PipeReader transports
|
|
/// schedule this on a background task while the deserialization context reads from the same
|
|
/// input on another thread.</para>
|
|
///
|
|
/// <para><see cref="AsyncPipeReaderInput.Complete"/> is invoked in a <c>finally</c> block —
|
|
/// ensures the consumer always wakes up even if the pipe read throws or the operation is
|
|
/// cancelled. Exceptions (including <see cref="OperationCanceledException"/>) propagate to
|
|
/// the caller after <c>Complete</c> runs.</para>
|
|
/// </summary>
|
|
/// <param name="input">The receive-side input to feed.</param>
|
|
/// <param name="reader">The pipe reader to drain.</param>
|
|
/// <param name="cancellationToken">Optional cancellation token.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="input"/> or <paramref name="reader"/> is <c>null</c>.</exception>
|
|
public static async Task DrainFromAsync(this AsyncPipeReaderInput input, PipeReader reader, CancellationToken cancellationToken = default)
|
|
{
|
|
if (input is null) throw new ArgumentNullException(nameof(input));
|
|
if (reader is null) throw new ArgumentNullException(nameof(reader));
|
|
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
var result = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
foreach (var segment in result.Buffer) input.Feed(segment.Span);
|
|
|
|
reader.AdvanceTo(result.Buffer.End);
|
|
if (result.IsCompleted) break;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
input.Complete();
|
|
}
|
|
}
|
|
}
|