// 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() { CustomCodec(); Console.WriteLine("PASS custom-codec"); } private static void CustomCodec() { // A caller-defined type: a protobuf-style varint registered under the name a definition uses. var codecs = new List { new Varint() }; var layout = new CStruct( "struct entry { varint id; uint8 kind; }; struct root { varint count; entry items[count]; };", compilationOptions: new CStructCompilationOptions { Codecs = codecs }); byte[] bytes = [2, 0x80, 0x01, 7, 0x05, 9]; StructValue root = layout.Parse(bytes, "root"); Equal(128UL, root.Get("items[0].id")); Equal((byte)9, root.Get("items[1].kind")); SequenceEqual(bytes, layout.Serialize("root", root)); using var stream = new MemoryStream(bytes); Equal(4L, layout.ResolveAddress(stream, "root.items[1].id")); } private sealed class Varint : ICustomCodec { public string Name => "varint"; public int? FixedSize => null; public int Alignment => 1; // The library hands the codec the bytes from the value's start: all remaining input for memory sources, // a growing window for other streams. The codec says how many it used. public OperationStatus Read(ReadOnlySpan source, out object? value, out int bytesConsumed) { ulong result = 0; for (int index = 0; index < source.Length && index < 10; index++) { result |= (ulong)(source[index] & 0x7F) << (7 * index); if ((source[index] & 0x80) == 0) { value = result; bytesConsumed = index + 1; return OperationStatus.Done; } } value = null; bytesConsumed = 0; return source.Length >= 10 ? OperationStatus.InvalidData : OperationStatus.NeedMoreData; } // DestinationTooSmall asks for a larger window; ten bytes always suffice for a 64-bit varint. public OperationStatus Write(Span destination, object value, out int bytesWritten) { ulong remaining = Convert.ToUInt64(value, CultureInfo.InvariantCulture); bytesWritten = 0; do { if (bytesWritten == destination.Length) { return OperationStatus.DestinationTooSmall; } byte next = (byte)(remaining & 0x7F); remaining >>= 7; destination[bytesWritten++] = remaining == 0 ? next : (byte)(next | 0x80); } while (remaining != 0); return OperationStatus.Done; } } 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)}."); } } }