diff --git a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs index 1538ff0..dd3af7e 100644 --- a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs +++ b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs @@ -1899,15 +1899,11 @@ public class AcBinarySourceGenerator : IIncrementalGenerator sb.AppendLine($" internal static readonly {ci.ClassName}_GeneratedReader Instance = new();"); sb.AppendLine(); - // ReadObject — IGeneratedBinaryReader implementation - sb.AppendLine(" public object? ReadObject(AcBinaryDeserializer.BinaryDeserializationContext context, int depth, int cacheIndex)"); + // ReadProperties — reads all properties into an existing instance (mirrors WriteProperties) + sb.AppendLine(" public void ReadProperties(object value, AcBinaryDeserializer.BinaryDeserializationContext context, int depth)"); sb.AppendLine(" where TInput : struct, IBinaryInputBase"); sb.AppendLine(" {"); - sb.AppendLine($" var obj = new {ci.FullTypeName}();"); - sb.AppendLine(); - sb.AppendLine(" if (cacheIndex >= 0)"); - sb.AppendLine(" context.RegisterInternedValueAt(cacheIndex, obj);"); - sb.AppendLine(); + sb.AppendLine($" var obj = Unsafe.As<{ci.FullTypeName}>(value);"); // Emit property reads — markerless for primitive types, markered for the rest foreach (var p in ci.Properties) @@ -1916,7 +1912,17 @@ public class AcBinarySourceGenerator : IIncrementalGenerator EmitReadProp(sb, p, " ", ci.EnableMetadata); } + sb.AppendLine(" }"); sb.AppendLine(); + + // ReadObject — IGeneratedBinaryReader implementation (delegates to ReadProperties) + sb.AppendLine(" public object? ReadObject(AcBinaryDeserializer.BinaryDeserializationContext context, int depth, int cacheIndex)"); + sb.AppendLine(" where TInput : struct, IBinaryInputBase"); + sb.AppendLine(" {"); + sb.AppendLine($" var obj = new {ci.FullTypeName}();"); + sb.AppendLine(" if (cacheIndex >= 0)"); + sb.AppendLine(" context.RegisterInternedValueAt(cacheIndex, obj);"); + sb.AppendLine(" ReadProperties(obj, context, depth);"); sb.AppendLine(" return obj;"); sb.AppendLine(" }"); sb.AppendLine("}"); @@ -2103,24 +2109,45 @@ public class AcBinarySourceGenerator : IIncrementalGenerator if (!p.ChildNeedsRefScan) { // Compile-time proven: child never tracked → only Object (+ Null for nullable) in stream + // Inline: parent creates instance, calls ReadProperties directly (mirrors EmitDirectObjectWrite) if (p.IsNullable) { sb.AppendLine($"{i}if ({tc} == BinaryTypeCode.Null) {{ /* null */ }}"); - sb.AppendLine($"{i}else {a} = {cast}{reader}.Instance.ReadObject(context, {nd}, -1)!;"); + sb.AppendLine($"{i}else"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});"); + sb.AppendLine($"{i} {a} = rc_{p.Name};"); + sb.AppendLine($"{i}}}"); } else { - // ZERO branches — tc is always Object, just call ReadObject - sb.AppendLine($"{i}{a} = {cast}{reader}.Instance.ReadObject(context, {nd}, -1)!;"); + // ZERO branches — tc is always Object + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});"); + sb.AppendLine($"{i} {a} = rc_{p.Name};"); + sb.AppendLine($"{i}}}"); } } else { // Ref tracking possible — Object/ObjectRefFirst/ObjectRef dispatch + // Inline: parent creates instance + handles cache registration sb.AppendLine($"{i}if ({tc} == BinaryTypeCode.Object)"); - sb.AppendLine($"{i} {a} = {cast}{reader}.Instance.ReadObject(context, {nd}, -1)!;"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});"); + sb.AppendLine($"{i} {a} = rc_{p.Name};"); + sb.AppendLine($"{i}}}"); sb.AppendLine($"{i}else if ({tc} == BinaryTypeCode.ObjectRefFirst)"); - sb.AppendLine($"{i} {a} = {cast}{reader}.Instance.ReadObject(context, {nd}, (int)context.ReadVarUInt())!;"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var ci_{p.Name} = (int)context.ReadVarUInt();"); + sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();"); + sb.AppendLine($"{i} context.RegisterInternedValueAt(ci_{p.Name}, rc_{p.Name});"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});"); + sb.AppendLine($"{i} {a} = rc_{p.Name};"); + sb.AppendLine($"{i}}}"); if (p.IsNullable) sb.AppendLine($"{i}else if ({tc} == BinaryTypeCode.Null) {{ /* null */ }}"); sb.AppendLine($"{i}else if ({tc} == BinaryTypeCode.ObjectRef)"); @@ -2219,9 +2246,19 @@ public class AcBinarySourceGenerator : IIncrementalGenerator sb.AppendLine($"{i} var {vtc} = context.ReadByte();"); sb.AppendLine($"{i} {valType}? dv_{s} = null;"); sb.AppendLine($"{i} if ({vtc} == BinaryTypeCode.Object)"); - sb.AppendLine($"{i} dv_{s} = ({valType}){valReader}.Instance.ReadObject(context, nd_{s}, -1)!;"); + sb.AppendLine($"{i} {{"); + sb.AppendLine($"{i} var rv_{s} = new {valType}();"); + sb.AppendLine($"{i} {valReader}.Instance.ReadProperties(rv_{s}, context, nd_{s});"); + sb.AppendLine($"{i} dv_{s} = rv_{s};"); + sb.AppendLine($"{i} }}"); sb.AppendLine($"{i} else if ({vtc} == BinaryTypeCode.ObjectRefFirst)"); - sb.AppendLine($"{i} dv_{s} = ({valType}){valReader}.Instance.ReadObject(context, nd_{s}, (int)context.ReadVarUInt())!;"); + sb.AppendLine($"{i} {{"); + sb.AppendLine($"{i} var rci_{s} = (int)context.ReadVarUInt();"); + sb.AppendLine($"{i} var rv_{s} = new {valType}();"); + sb.AppendLine($"{i} context.RegisterInternedValueAt(rci_{s}, rv_{s});"); + sb.AppendLine($"{i} {valReader}.Instance.ReadProperties(rv_{s}, context, nd_{s});"); + sb.AppendLine($"{i} dv_{s} = rv_{s};"); + sb.AppendLine($"{i} }}"); sb.AppendLine($"{i} else if ({vtc} == BinaryTypeCode.ObjectRef)"); sb.AppendLine($"{i} dv_{s} = ({valType})context.GetInternedObject((int)context.ReadVarUInt())!;"); sb.AppendLine($"{i} else if ({vtc} != BinaryTypeCode.Null)"); @@ -2376,7 +2413,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator sb.AppendLine($"{i} for (var ri_{s} = 0; ri_{s} < cnt_{s}; ri_{s}++)"); sb.AppendLine($"{i} {{"); if (isComplexElement) - EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), $"({elemType})", $"ri_{s}", s, i + " ", isArray: true, p.ElementNeedsRefScan); + EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), elemType, $"({elemType})", $"ri_{s}", s, i + " ", isArray: true, p.ElementNeedsRefScan); else EmitReadNonComplexCollectionElement(sb, p, $"ri_{s}", s, i + " ", isArray: true, null); sb.AppendLine($"{i} }}"); @@ -2391,7 +2428,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator sb.AppendLine($"{i} for (var ri_{s} = 0; ri_{s} < cnt_{s}; ri_{s}++)"); sb.AppendLine($"{i} {{"); if (isComplexElement) - EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), $"({elemType})", $"ri_{s}", s, i + " ", isArray: false, p.ElementNeedsRefScan, p.CollectionAddMethod); + EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), elemType, $"({elemType})", $"ri_{s}", s, i + " ", isArray: false, p.ElementNeedsRefScan, p.CollectionAddMethod); else EmitReadNonComplexCollectionElement(sb, p, $"ri_{s}", s, i + " ", isArray: false, p.CollectionAddMethod); sb.AppendLine($"{i} }}"); @@ -2402,7 +2439,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator sb.AppendLine($"{i} for (var ri_{s} = 0; ri_{s} < cnt_{s}; ri_{s}++)"); sb.AppendLine($"{i} {{"); if (isComplexElement) - EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), $"({elemType})", $"ri_{s}", s, i + " ", isArray: false, p.ElementNeedsRefScan); + EmitReadCollectionElement(sb, p.ElementWriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader"), elemType, $"({elemType})", $"ri_{s}", s, i + " ", isArray: false, p.ElementNeedsRefScan); else EmitReadNonComplexCollectionElement(sb, p, $"ri_{s}", s, i + " ", isArray: false, null); sb.AppendLine($"{i} }}"); @@ -2417,32 +2454,49 @@ public class AcBinarySourceGenerator : IIncrementalGenerator /// SGen reader = non-metadata mode → no ObjectWithMetadata fallback. /// !needsRefScan → only Object/Null possible → 1 branch per element. /// - private static void EmitReadCollectionElement(StringBuilder sb, string reader, string elemCast, string indexVar, string propSuffix, string i, bool isArray, bool needsRefScan, string? addMethod = null) + private static void EmitReadCollectionElement(StringBuilder sb, string reader, string elemTypeName, string elemCast, string indexVar, string propSuffix, string i, bool isArray, bool needsRefScan, string? addMethod = null) { var etc = $"etc_{propSuffix}"; sb.AppendLine($"{i}var {etc} = context.ReadByte();"); var addCall = addMethod ?? "Add"; var assignNull = isArray ? $"col_{propSuffix}[{indexVar}] = null!;" : $"col_{propSuffix}.{addCall}(null!);"; - var assignExpr = isArray ? $"col_{propSuffix}[{indexVar}] = " : $"col_{propSuffix}.{addCall}("; - var assignEnd = isArray ? ";" : ");"; + var assignExpr = isArray ? $"col_{propSuffix}[{indexVar}] = re_{propSuffix};" : $"col_{propSuffix}.{addCall}(re_{propSuffix});"; if (!needsRefScan) { - // No ref tracking → only Object or Null in stream — 1 branch + // No ref tracking → only Object or Null in stream — inline ReadProperties sb.AppendLine($"{i}if ({etc} == BinaryTypeCode.Null) {{ {assignNull} }}"); - sb.AppendLine($"{i}else {assignExpr}{elemCast}{reader}.Instance.ReadObject(context, nd_{propSuffix}, -1)!{assignEnd}"); + sb.AppendLine($"{i}else"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var re_{propSuffix} = new {elemTypeName}();"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context, nd_{propSuffix});"); + sb.AppendLine($"{i} {assignExpr}"); + sb.AppendLine($"{i}}}"); } else { - // Object hot path first, then ref markers + // Object hot path first, then ref markers — inline ReadProperties sb.AppendLine($"{i}if ({etc} == BinaryTypeCode.Object)"); - sb.AppendLine($"{i} {assignExpr}{elemCast}{reader}.Instance.ReadObject(context, nd_{propSuffix}, -1)!{assignEnd}"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var re_{propSuffix} = new {elemTypeName}();"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context, nd_{propSuffix});"); + sb.AppendLine($"{i} {assignExpr}"); + sb.AppendLine($"{i}}}"); sb.AppendLine($"{i}else if ({etc} == BinaryTypeCode.ObjectRefFirst)"); - sb.AppendLine($"{i} {assignExpr}{elemCast}{reader}.Instance.ReadObject(context, nd_{propSuffix}, (int)context.ReadVarUInt())!{assignEnd}"); + sb.AppendLine($"{i}{{"); + sb.AppendLine($"{i} var ci_{propSuffix} = (int)context.ReadVarUInt();"); + sb.AppendLine($"{i} var re_{propSuffix} = new {elemTypeName}();"); + sb.AppendLine($"{i} context.RegisterInternedValueAt(ci_{propSuffix}, re_{propSuffix});"); + sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context, nd_{propSuffix});"); + sb.AppendLine($"{i} {assignExpr}"); + sb.AppendLine($"{i}}}"); sb.AppendLine($"{i}else if ({etc} == BinaryTypeCode.Null) {{ {assignNull} }}"); sb.AppendLine($"{i}else if ({etc} == BinaryTypeCode.ObjectRef)"); - sb.AppendLine($"{i} {assignExpr}{elemCast}context.GetInternedObject((int)context.ReadVarUInt())!{assignEnd}"); + if (isArray) + sb.AppendLine($"{i} col_{propSuffix}[{indexVar}] = {elemCast}context.GetInternedObject((int)context.ReadVarUInt())!;"); + else + sb.AppendLine($"{i} col_{propSuffix}.{addCall}({elemCast}context.GetInternedObject((int)context.ReadVarUInt())!);"); } } diff --git a/AyCode.Core/Serializers/Binaries/IGeneratedBinaryReader.cs b/AyCode.Core/Serializers/Binaries/IGeneratedBinaryReader.cs index 5e63aee..675e1a9 100644 --- a/AyCode.Core/Serializers/Binaries/IGeneratedBinaryReader.cs +++ b/AyCode.Core/Serializers/Binaries/IGeneratedBinaryReader.cs @@ -26,4 +26,16 @@ internal interface IGeneratedBinaryReader /// The deserialized object, or null if creation failed. object? ReadObject(AcBinaryDeserializer.BinaryDeserializationContext context, int depth, int cacheIndex) where TInput : struct, IBinaryInputBase; + + /// + /// Reads all properties into an existing instance (no object creation, no cache registration). + /// Called from parent SGen reader after instance creation + cache registration. + /// Mirrors WriteProperties: parent handles lifecycle, child handles data. + /// + /// The pre-created instance to populate. Implementation casts to the concrete type. + /// The deserialization context (owns buffer, position, options). + /// Current depth in the object graph. + /// Input strategy (ArrayBinaryInput or SequenceBinaryInput). + void ReadProperties(object value, AcBinaryDeserializer.BinaryDeserializationContext context, int depth) + where TInput : struct, IBinaryInputBase; }