// 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 void Main() { TryParseForms(); Console.WriteLine("PASS try-parse"); } private static void TryParseForms() { byte[] bytes = [0x02, 0x00, 0x06, 0x00, 0x00, 0x00]; // TryParse returns false instead of throwing for a read, path, or limit failure; the value is null then. True(Wire.TryParse(bytes, out Wire.Header? header) && header.Length == 6, "six bytes are a header"); True(!Wire.TryParse(bytes.AsSpan(0, 3), out Wire.Header? missing) && missing is null, "three bytes are not"); // The second form hands over the failure the throwing form would have raised, so a log line can say why. True(!Wire.TryParse(bytes.AsSpan(0, 3), out _, out CStructException? failure), "the same outcome"); True(failure is CStructReadException, "a short read: the kind was read, the length needs four more bytes"); True(failure!.Message.Contains("needed 4, available 1", StringComparison.Ordinal), failure.Message); // A stream is left at its origin after a failure, so the caller can try another layout at the same place. using var stream = new MemoryStream([0xFF, 0x02, 0x00, 0x06]) { Position = 1 }; True(!Wire.TryParse(stream, out _), "three bytes remain"); Equal(1L, stream.Position); // Cancellation is not a failure: it throws through TryParse as it does through Parse. using var cancelled = new CancellationTokenSource(); cancelled.Cancel(); Throws(() => Wire.TryParse(bytes, out _, new ReadOptions { CancellationToken = cancelled.Token })); // The runtime's TryReadValue is the same idea for a path; TryGet and GetOrDefault are its members' twins. True(Wire.Layout.TryReadValue(bytes, "header.length", out uint length) && length == 6, "typed path read"); StructValue parsed = Wire.Layout.Parse(bytes, "header"); True(!parsed.TryGet("flags", out byte _, out CStructException? absent) && absent is CStructPathException, "no such member"); Equal((byte)0, parsed.GetOrDefault("flags", (byte)0)); } 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); } } private static void Throws(Action action) where TException : Exception { try { action(); } catch (TException) { return; } throw new InvalidOperationException($"Expected {typeof(TException).Name}."); } // The layout text lives in the attribute; the generator adds the members of this partial class at build time. [CStructLayout("struct header { uint16 kind; uint32 length; };")] public static partial class Wire { } }