using System.Runtime.CompilerServices; using AyCode.Core.Serializers.Binaries; using AyCode.Core.Tests.TestModels; namespace AyCode.Core.Tests.GeneratedWriters; /// /// Hand-written generated binary writer for TestOrder. /// Demonstrates the pattern that the source generator will produce. /// /// Bypasses the runtime switch/delegate property loop: /// - Direct obj.Property access instead of Func<>.Invoke() /// - No switch dispatch per property /// - No boxing for value types /// - Small method (~500B native) vs 27KB WriteObject — better ICache /// /// Properties are written in alphabetical order to match the runtime serializer. /// Complex/Collection properties fall back to the runtime serializer via WriteValue. /// internal sealed class TestOrderWriter : IGeneratedBinaryWriter { internal static readonly TestOrderWriter Instance = new(); public void WriteProperties( object value, AcBinarySerializer.BinarySerializationContext context, int depth) where TOutput : struct, IBinaryOutputBase { var obj = Unsafe.As(value); var nextDepth = depth; // Properties in alphabetical order (matching runtime serializer): // AuditMetadata: MetadataInfo? (complex, nullable) WriteComplexOrNull(obj.AuditMetadata, context, nextDepth); // Category: SharedCategory? (complex, nullable) WriteComplexOrNull(obj.Category, context, nextDepth); // CreatedAt: DateTime context.WriteByte(BinaryTypeCode.DateTime); context.WriteDateTimeBits(obj.CreatedAt); // Id: int WriteInt32OrSkip(obj.Id, context); // Items: List (collection) WriteComplexOrNull(obj.Items, context, nextDepth); // MetadataList: List (collection) WriteComplexOrNull(obj.MetadataList, context, nextDepth); // NoMergeItems: List (collection) WriteComplexOrNull(obj.NoMergeItems, context, nextDepth); // OrderMetadata: MetadataInfo? (complex, nullable) WriteComplexOrNull(obj.OrderMetadata, context, nextDepth); // OrderNumber: string AcBinarySerializer.WriteStringGenerated(obj.OrderNumber, context); // Owner: SharedUser? (complex, nullable) WriteComplexOrNull(obj.Owner, context, nextDepth); // PaidDateUtc: DateTime? (nullable) var paidDate = obj.PaidDateUtc; if (paidDate.HasValue) { context.WriteByte(BinaryTypeCode.DateTime); context.WriteDateTimeBits(paidDate.Value); } else { context.WriteByte(BinaryTypeCode.Null); } // PrimaryTag: SharedTag? (complex, nullable) WriteComplexOrNull(obj.PrimaryTag, context, nextDepth); // SecondaryTag: SharedTag? (complex, nullable) WriteComplexOrNull(obj.SecondaryTag, context, nextDepth); // Status: TestStatus (enum) context.WriteByte(BinaryTypeCode.Enum); var enumVal = (int)obj.Status; if (BinaryTypeCode.TryEncodeTinyInt(enumVal, out var tinyEnum)) context.WriteByte(tinyEnum); else { context.WriteByte(BinaryTypeCode.Int32); context.WriteVarInt(enumVal); } // Tags: List (collection) WriteComplexOrNull(obj.Tags, context, nextDepth); // TotalAmount: decimal if (obj.TotalAmount == 0m) context.WriteByte(BinaryTypeCode.PropertySkip); else { context.WriteByte(BinaryTypeCode.Decimal); context.WriteDecimalBits(obj.TotalAmount); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WriteInt32OrSkip(int value, AcBinarySerializer.BinarySerializationContext context) where TOutput : struct, IBinaryOutputBase { if (value == 0) { context.WriteByte(BinaryTypeCode.PropertySkip); return; } if (BinaryTypeCode.TryEncodeTinyInt(value, out var tiny)) { context.WriteByte(BinaryTypeCode.Int32); context.WriteByte(tiny); return; } context.WriteByte(BinaryTypeCode.Int32); context.WriteVarInt(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WriteComplexOrNull(object? value, AcBinarySerializer.BinarySerializationContext context, int depth) where TOutput : struct, IBinaryOutputBase { if (value == null) { context.WriteByte(BinaryTypeCode.PropertySkip); return; } AcBinarySerializer.WriteValueGenerated(value, value.GetType(), context, depth); } }