// Generated from executable documentation examples. Edit the source region, then regenerate.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Buffers;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CStructSharp;
using CStructSharp.Codecs;
using CStructSharp.Diagnostics;
using CStructSharp.Introspection;
using CStructSharp.Values;
internal static partial class Program
{
public static async Task Main()
{
await AsyncStream();
Console.WriteLine("PASS async-stream");
}
/// Parses a file asynchronously and verifies failure, cancellation and stream positioning.
private static async Task AsyncStream()
{
// A file holds a six-byte header and a payload. Opened for asynchronous I/O, the header is read with
// ParseAsync: the bytes arrive through ReadAsync while the thread is free, then the ordinary reader decodes them.
var layout = new CStruct("struct header { uint16 kind; uint32 length; };");
string path = Path.Combine(Path.GetTempPath(), $"cstructsharp-{Guid.NewGuid():N}.bin");
await File.WriteAllBytesAsync(path, [0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
try
{
await using var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true);
// A token bounds the wait: a stalled disk or network ends in OperationCanceledException, not a read failure.
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
StructValue header = await layout.ParseAsync(file, "header", cancellationToken: timeout.Token);
Equal((ushort)2, header.Get("kind"));
Equal(6u, header.Get("length"));
Equal(6L, file.Position);
// The payload's length came from the header; the rest of the file is read the usual way.
byte[] payload = new byte[header.Get("length")];
await file.ReadExactlyAsync(payload, timeout.Token);
Equal("AABBCCDDEEFF", Convert.ToHexString(payload));
// Three bytes at the end are not a header: the non-throwing form reports the failure the throwing form
// would raise, and a seekable stream is back where it started.
file.Position = 9;
ReadAttempt attempt = await layout.TryReadValueAsync(file, "header", cancellationToken: timeout.Token);
True(!attempt.Succeeded, "three bytes are not a header");
True(attempt.Failure is CStructReadException, "the failure is the read exception");
Equal(9L, file.Position);
// A cancelled token stops the read before any byte is taken.
using var cancelled = new CancellationTokenSource();
cancelled.Cancel();
file.Position = 0;
try
{
await layout.ParseAsync(file, "header", cancellationToken: cancelled.Token);
True(false, "unreachable");
}
catch (OperationCanceledException)
{
Equal(0L, file.Position);
}
}
finally
{
File.Delete(path);
}
}
private static void Equal(T expected, T actual)
{
if (!EqualityComparer.Default.Equals(expected, actual))
{
throw new InvalidOperationException($"Expected '{expected}', received '{actual}'.");
}
}
private static void True(bool condition, string message)
{
if (!condition)
{
throw new InvalidOperationException(message);
}
}
}