AyCode.Core/AyCode.Core/Serializers/Binaries/AcBinarySerializer.NamedPip...

73 lines
3.7 KiB
C#

using System;
using System.IO.Pipelines;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
namespace AyCode.Core.Serializers.Binaries;
public static partial class AcBinarySerializer
{
/// <summary>
/// Serializes <paramref name="value"/> to a Windows / Linux named-pipe server using the
/// AsyncSegment chunked wire format (<c>[201][UINT16 size][data]</c> per chunk via
/// <see cref="AsyncPipeWriterOutput"/>). One-shot client lifecycle: connects, streams
/// chunk-by-chunk with per-chunk <c>FlushAsync</c> for real producer/consumer overlap,
/// completes, disposes.
///
/// <para><b>Wire format</b>: same chunked AsyncSegment framing as SignalR uses internally —
/// unified format across all transports per ADR-0003 §9. The <c>+5 bytes/chunk</c> overhead
/// (~0.1% at 4 KB chunks, ~2% at 256-byte test chunks) is the cost of a single shared wire
/// format and a single framing-strip implementation (in <see cref="AsyncPipeReaderInput.Feed"/>).</para>
///
/// <para><b>Streaming behavior</b>: every <c>BufferWriterChunkSize</c>-sized chunk is
/// flushed to the pipe immediately (per-chunk <c>SyncAwaitFlush</c>). Consumer can start
/// reading WHILE producer is still serializing — true pipeline parallelism even on small
/// payloads (no buffer-accumulation-then-flush behavior).</para>
///
/// <para><b>Cross-platform</b>: NamedPipe BCL APIs work on Windows and Linux (Unix-domain-
/// socket-backed on Linux). WASM throws <see cref="PlatformNotSupportedException"/> per
/// BCL contract.</para>
///
/// <para><b>For custom connection management</b> (multiple writes, custom NamedPipe options,
/// pre-existing connection): use
/// <see cref="Serialize{T}(T, PipeWriter, AcBinarySerializerOptions, bool, TimeSpan?)"/>
/// directly on a <see cref="PipeWriter"/> wrapping your own
/// <see cref="NamedPipeClientStream"/>.</para>
/// </summary>
/// <param name="pipeName">Pipe name to connect to.</param>
/// <param name="value">Object to serialize.</param>
/// <param name="options">Serializer options. Defaults to <see cref="AcBinarySerializerOptions.Default"/>.
/// <c>BufferWriterChunkSize</c> controls the wire chunk size (max 65535).</param>
/// <param name="serverName">NamedPipe server host. Defaults to <c>"."</c> (local machine).</param>
/// <param name="ct">Cancellation token. For connect-timeout, pass the token of a
/// <c>new CancellationTokenSource(timeout)</c> — uniform cancellation/timeout pattern.</param>
public static async Task SerializeToNamedPipeAsync<T>(
string pipeName,
T value,
AcBinarySerializerOptions? options = null,
string serverName = ".",
CancellationToken ct = default)
{
if (pipeName is null) throw new ArgumentNullException(nameof(pipeName));
if (serverName is null) throw new ArgumentNullException(nameof(serverName));
await using var pipeClient = new NamedPipeClientStream(serverName, pipeName, PipeDirection.Out, System.IO.Pipes.PipeOptions.Asynchronous);
await pipeClient.ConnectAsync(ct).ConfigureAwait(false);
var pipeWriter = PipeWriter.Create(pipeClient);
try
{
// PipeWriter overload — chunked AsyncSegment framing via AsyncPipeWriterOutput.
// Receiver's AsyncPipeReaderInput.Feed strips framing internally; unified wire format
// across all transports per ADR-0003 §9.
Serialize(value, pipeWriter, options ?? AcBinarySerializerOptions.Default);
}
finally
{
await pipeWriter.CompleteAsync().ConfigureAwait(false);
}
}
}