// 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() { ConditionalRecords(); Console.WriteLine("PASS conditional-records"); } private static void ConditionalRecords() { const string definition = """ struct entry { uint8 kind; switch (kind) { case 1: { utf8 label[3]; } default: { uint24< number; } } if (kind == 1) { uint8 flags; } }; struct root { uleb128_32 count; entry items[count]; }; """; var layout = new CStruct(definition, aligned: false); byte[] bytes = [2, 1, 226, 130, 172, 7, 2, 42, 0, 0]; StructValue parsed = layout.Parse(bytes, "root"); Equal("€", parsed.Get("items[0].label")); Equal(42U, parsed.Get("items[1].number")); SequenceEqual(bytes, layout.Serialize("root", parsed)); using var stream = new MemoryStream(bytes); Equal(7L, layout.ResolveAddress(stream, "root.items[1].number")); Throws(() => layout.ResolveAddress(stream, "root.items[1].label")); Throws(() => layout.Update(stream, "root.items[0].kind", 2)); SequenceEqual(bytes, stream.ToArray()); // Each item evaluates both groups with its own fields, including a calculation. const string decisions = "struct entry { uint8 tag; int8 some_parameter; if (some_parameter * 20 > 10) { uint8 high; } else { uint8 low; } switch (tag) { case 1: { uint8 first; } case 2: { uint8 second; } default: { uint8 other; } } }; struct root { entry items[3]; };"; var decisionLayout = new CStruct(decisions, aligned: false); byte[] items = [1, 0, 10, 11, 2, 1, 20, 21, 3, 255, 30, 31]; StructValue selected = decisionLayout.Parse(items, "root"); Equal((byte)10, selected.Get("items[0].low")); Equal((byte)21, selected.Get("items[1].second")); Equal((byte)31, selected.Get("items[2].other")); SequenceEqual(items, decisionLayout.Serialize("root", selected)); items[0] = 2; Equal((byte)11, decisionLayout.Parse(items, "root").Get("items[0].second")); items[0] = 1; items[1] = 1; Equal((byte)10, decisionLayout.Parse(items, "root").Get("items[0].high")); // A caller's count cannot replace an unread local; a short-circuit guard repairs the example. const string scope = "#define count 99\nstruct entry { uint8 tag; if (tag) { uint8 count; } if (count > 0) { uint8 payload[count]; } }; struct root { entry items[2]; };"; byte[] scopedBytes = [1, 1, 42, 0]; var variables = new Dictionary { ["count"] = 99 }; var scopedLayout = new CStruct(scope, aligned: false); Throws(() => scopedLayout.Parse(scopedBytes, "root", variables: variables)); var guarded = new CStruct(scope.Replace("if (count > 0)", "if (tag != 0 && count > 0)"), aligned: false); StructValue scoped = guarded.Parse(scopedBytes, "root", variables: variables); Equal(1, scoped.Get("items[1]").Count); SequenceEqual(scopedBytes, guarded.Serialize("root", scoped, variables: variables)); // Inactive nested expressions are skipped, but become errors when reached. var nested = new CStruct("struct root { uint8 tag; if (tag) { if (missing > 0) { uint8 value; } } uint8 tail; };", aligned: false); Equal((byte)9, nested.Parse(new byte[] { 0, 9 }, "root").Get("tail")); Throws(() => nested.Parse(new byte[] { 1, 9 }, "root")); } private static void Equal(T expected, T actual) { if (!EqualityComparer.Default.Equals(expected, actual)) { throw new InvalidOperationException($"Expected '{expected}', received '{actual}'."); } } private static void SequenceEqual(byte[] expected, byte[] actual) { if (!expected.AsSpan().SequenceEqual(actual)) { throw new InvalidOperationException( $"Expected {Convert.ToHexString(expected)}, received {Convert.ToHexString(actual)}."); } } private static void Throws(Action action) where TException : Exception { try { action(); } catch (TException) { return; } throw new InvalidOperationException($"Expected {typeof(TException).Name}."); } }