// 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); } } }