Remove depth param from serializers; use context field
Refactored AcBinary, AcJson, and AcToon serializers to eliminate the explicit depth parameter from all serialization/deserialization methods, generated code, and interfaces. Introduced a global RecursionDepth field on the serialization context, incremented/decremented at recursion entry/exit, and enforced against MaxDepth as a safety net (except when ReferenceHandling=All). Updated all usages, including property, array, and dictionary handling, to use the new context-based depth tracking. Ensured consistency across runtime and generated code.
This commit is contained in:
parent
b849beb2ee
commit
ac6e66f59f
File diff suppressed because one or more lines are too long
|
|
@ -542,8 +542,18 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.Append(string.Join(", ", ci.PropertyNameHashes));
|
||||
sb.AppendLine(" };");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public void WriteProperties<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth) where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" public void WriteProperties<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" // Global recursion depth safety net — only when ReferenceHandling != All");
|
||||
sb.AppendLine(" // (HasAllRefHandling=true tracks every type → write plan already prevents cycles via ObjectRef).");
|
||||
sb.AppendLine(" var needsDepthCheck = !context.HasAllRefHandling;");
|
||||
sb.AppendLine(" if (needsDepthCheck)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" if (context.RecursionDepth >= context.MaxDepth)");
|
||||
sb.AppendLine($" throw new System.InvalidOperationException(\"AcBinary serialize: recursion depth exceeded MaxDepth=\" + context.MaxDepth + \" at {ci.FullTypeName}\");");
|
||||
sb.AppendLine(" context.RecursionDepth++;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($" var obj = Unsafe.As<{ci.FullTypeName}>(value);");
|
||||
|
||||
foreach (var p in ci.Properties)
|
||||
|
|
@ -552,6 +562,8 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
EmitProp(sb, p, " ", ci.FullTypeName, ci.EnableMetadata, ci.EnablePropertyFilter);
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" if (needsDepthCheck) context.RecursionDepth--;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
|
||||
|
|
@ -565,7 +577,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine(" where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" if (!context.HasCaching) return;");
|
||||
sb.AppendLine(" ScanObject(value, context, 0);");
|
||||
sb.AppendLine(" ScanObject(value, context);");
|
||||
sb.AppendLine(" context.SortWritePlan();");
|
||||
sb.AppendLine(" }");
|
||||
|
||||
|
|
@ -580,7 +592,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
/// </summary>
|
||||
private static void GenScanProperties(StringBuilder sb, SerializableClassInfo ci)
|
||||
{
|
||||
sb.AppendLine(" public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth) where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
// Compile-time proven: no scan work needed for this type
|
||||
|
|
@ -602,8 +614,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine(" if (!context.HasStringInterning) return;");
|
||||
}
|
||||
|
||||
// Null/depth guard — matches runtime ScanValue entry
|
||||
sb.AppendLine(" if (value == null || depth > context.MaxDepth) return;");
|
||||
// Null guard — MaxDepth option removed (was: cycle protection via runtime depth check).
|
||||
// Cycle safety now comes from IId-tracking; future [AcBinaryCircular] attr will mark non-IId circular refs.
|
||||
sb.AppendLine(" if (value == null) return;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($" var obj = Unsafe.As<{ci.FullTypeName}>(value);");
|
||||
|
||||
|
|
@ -663,6 +676,19 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
(p.TypeKind == PropertyTypeKind.Collection && p.ElementKind == PropertyTypeKind.String && p.InterningFlags != 0) ||
|
||||
(p.TypeKind == PropertyTypeKind.Dictionary && (p.DictKeyKind == PropertyTypeKind.String || p.DictValueKind == PropertyTypeKind.String) && p.InterningFlags != 0));
|
||||
|
||||
// Global recursion depth safety net — only when ReferenceHandling != All
|
||||
// (HasAllRefHandling=true tracks every type → 2nd-occurrence early-return already prevents cycles).
|
||||
// Emitted AFTER all early returns (NeedsScan=false, feature-flag, null guard, IId 2nd-occurrence)
|
||||
// and BEFORE the property scan loop that recurses into children.
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" var needsDepthCheck = !context.HasAllRefHandling;");
|
||||
sb.AppendLine(" if (needsDepthCheck)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" if (context.RecursionDepth >= context.MaxDepth)");
|
||||
sb.AppendLine($" throw new System.InvalidOperationException(\"AcBinary scan: recursion depth exceeded MaxDepth=\" + context.MaxDepth + \" at {ci.FullTypeName}\");");
|
||||
sb.AppendLine(" context.RecursionDepth++;");
|
||||
sb.AppendLine(" }");
|
||||
|
||||
if (hasStringScan)
|
||||
{
|
||||
// Use pre-computed InternBit from context (avoids Options.UseStringInterning field chain + shift per object).
|
||||
|
|
@ -688,6 +714,8 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine(" // No reference properties to scan");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" if (needsDepthCheck) context.RecursionDepth--;");
|
||||
sb.AppendLine(" }");
|
||||
}
|
||||
|
||||
|
|
@ -774,16 +802,16 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} context.WriteStringUtf8({a}.GetType().AssemblyQualifiedName!);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
sb.AppendLine($"{i} AcBinarySerializer.WriteValueGenerated({a}, {a}.GetType(), context, depth);");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.WriteValueGenerated({a}, {a}.GetType(), context);");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
else if (p.IsNullable)
|
||||
{
|
||||
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
||||
sb.AppendLine($"{i}else AcBinarySerializer.WriteObjectGenerated({a}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i}else AcBinarySerializer.WriteObjectGenerated({a}, typeof({p.TypeNameForTypeof}), context);");
|
||||
}
|
||||
else
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteObjectGenerated({a}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteObjectGenerated({a}, typeof({p.TypeNameForTypeof}), context);");
|
||||
break;
|
||||
case PropertyTypeKind.Collection:
|
||||
// Direct collection write for List<T>/T[] with Complex element types that have generated writers
|
||||
|
|
@ -792,10 +820,10 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
else if (p.IsNullable)
|
||||
{
|
||||
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
||||
sb.AppendLine($"{i}else AcBinarySerializer.WriteValueGenerated({a}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i}else AcBinarySerializer.WriteValueGenerated({a}, typeof({p.TypeNameForTypeof}), context);");
|
||||
}
|
||||
else
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, typeof({p.TypeNameForTypeof}), context);");
|
||||
break;
|
||||
case PropertyTypeKind.Dictionary:
|
||||
EmitDirectDictionaryWrite(sb, p, a, i);
|
||||
|
|
@ -1024,7 +1052,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} var {childVar} = {a};");
|
||||
sb.AppendLine($"{i} if ({childVar} != null)");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context, depth + 1);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context);");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
else
|
||||
|
|
@ -1032,7 +1060,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
// IId in subtree — always call (active in OnlyId + All)
|
||||
sb.AppendLine($"{i}var {childVar} = {a};");
|
||||
sb.AppendLine($"{i}if ({childVar} != null)");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context, depth + 1);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context);");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1046,9 +1074,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}var {childVar} = {a};");
|
||||
sb.AppendLine($"{i}if ({childVar} != null)");
|
||||
if (p.IsObjectDeclaredType)
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated({childVar}, {childVar}.GetType(), context, depth + 1);");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated({childVar}, {childVar}.GetType(), context);");
|
||||
else
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated({childVar}, typeof({p.TypeNameForTypeof}), context, depth + 1);");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated({childVar}, typeof({p.TypeNameForTypeof}), context);");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1130,7 +1158,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} var scol_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i} if (scol_{p.Name} != null)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var snd_{p.Name} = depth + 2;");
|
||||
|
||||
if (p.CollectionKind == "Array")
|
||||
{
|
||||
|
|
@ -1160,7 +1187,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
var e = $"se_{p.Name}";
|
||||
// Null check only — ScanObject handles depth + ref tracking internally
|
||||
sb.AppendLine($"{i} if ({e} == null) continue;");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({e}, context, snd_{p.Name});");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({e}, context);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i}}}");
|
||||
|
|
@ -1172,7 +1199,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i}var scol_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i}if (scol_{p.Name} != null)");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated(scol_{p.Name}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.ScanValueGenerated(scol_{p.Name}, typeof({p.TypeNameForTypeof}), context);");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1253,7 +1280,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} var sd_{s} = {a};");
|
||||
sb.AppendLine($"{i} if (sd_{s} != null)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var snd_{s} = depth + 2;");
|
||||
sb.AppendLine($"{i} foreach (var sde_{s} in sd_{s})");
|
||||
sb.AppendLine($"{i} {{");
|
||||
|
||||
|
|
@ -1287,11 +1313,11 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} if ({complexGuard})");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject(sde_{s}.Value, context, snd_{s});");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject(sde_{s}.Value, context);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
else
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject(sde_{s}.Value, context, snd_{s});");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject(sde_{s}.Value, context);");
|
||||
}
|
||||
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -1313,24 +1339,23 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
|
||||
// Reference type properties can always be null at runtime regardless of nullable annotation
|
||||
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
||||
sb.AppendLine($"{i}else if (depth > context.MaxDepth) context.WriteByte(BinaryTypeCode.Null);");
|
||||
|
||||
if (!p.ChildNeedsRefScan && !p.ChildEnableMetadata)
|
||||
{
|
||||
// Compile-time proven: no ref, no metadata → ZERO branches: always Object + WriteProperties
|
||||
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Object); {writer}.Instance.WriteProperties({a}, context, depth + 1); }}");
|
||||
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Object); {writer}.Instance.WriteProperties({a}, context); }}");
|
||||
}
|
||||
else if (p.ChildNeedsRefScan && !p.ChildEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectRefMarker{refSuffix}()) {writer}.Instance.WriteProperties({a}, context, depth + 1);");
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectRefMarker{refSuffix}()) {writer}.Instance.WriteProperties({a}, context);");
|
||||
}
|
||||
else if (!p.ChildNeedsRefScan && p.ChildEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i}else {{ context.WriteObjectMetaMarker({a}, {writer}.s_wrapperSlot); {writer}.Instance.WriteProperties({a}, context, depth + 1); }}");
|
||||
sb.AppendLine($"{i}else {{ context.WriteObjectMetaMarker({a}, {writer}.s_wrapperSlot); {writer}.Instance.WriteProperties({a}, context); }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectFullMarker{refSuffix}({a}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({a}, context, depth + 1);");
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectFullMarker{refSuffix}({a}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({a}, context);");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1361,7 +1386,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
|
||||
// Reference type collections can always be null at runtime regardless of nullable annotation
|
||||
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
||||
sb.AppendLine($"{i}else if (depth > context.MaxDepth) context.WriteByte(BinaryTypeCode.Null);");
|
||||
sb.AppendLine($"{i}else");
|
||||
sb.AppendLine($"{i}{{");
|
||||
|
||||
|
|
@ -1372,8 +1396,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i} var arr_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)arr_{p.Name}.Length);");
|
||||
sb.AppendLine($"{i} var nextDepth_{p.Name} = depth + 2;");
|
||||
sb.AppendLine($"{i} var depthExceeded_{p.Name} = depth + 1 > context.MaxDepth;");
|
||||
sb.AppendLine($"{i} for (var i_{p.Name} = 0; i_{p.Name} < arr_{p.Name}.Length; i_{p.Name}++)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var elem_{p.Name} = arr_{p.Name}[i_{p.Name}];");
|
||||
|
|
@ -1382,8 +1404,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i} var col_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)col_{p.Name}.Count);");
|
||||
sb.AppendLine($"{i} var nextDepth_{p.Name} = depth + 2;");
|
||||
sb.AppendLine($"{i} var depthExceeded_{p.Name} = depth + 1 > context.MaxDepth;");
|
||||
sb.AppendLine($"{i} foreach (var elem_{p.Name} in col_{p.Name})");
|
||||
sb.AppendLine($"{i} {{");
|
||||
}
|
||||
|
|
@ -1391,8 +1411,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i} var col_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)col_{p.Name}.Count);");
|
||||
sb.AppendLine($"{i} var nextDepth_{p.Name} = depth + 2;");
|
||||
sb.AppendLine($"{i} var depthExceeded_{p.Name} = depth + 1 > context.MaxDepth;");
|
||||
sb.AppendLine($"{i} for (var i_{p.Name} = 0; i_{p.Name} < col_{p.Name}.Count; i_{p.Name}++)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var elem_{p.Name} = col_{p.Name}[i_{p.Name}];");
|
||||
|
|
@ -1401,8 +1419,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
sb.AppendLine($"{i} var span_{p.Name} = CollectionsMarshal.AsSpan({a});");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)span_{p.Name}.Length);");
|
||||
sb.AppendLine($"{i} var nextDepth_{p.Name} = depth + 2;");
|
||||
sb.AppendLine($"{i} var depthExceeded_{p.Name} = depth + 1 > context.MaxDepth;");
|
||||
sb.AppendLine($"{i} for (var i_{p.Name} = 0; i_{p.Name} < span_{p.Name}.Length; i_{p.Name}++)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var elem_{p.Name} = span_{p.Name}[i_{p.Name}];");
|
||||
|
|
@ -1410,7 +1426,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
|
||||
// Per-element write
|
||||
var e = $"elem_{p.Name}";
|
||||
sb.AppendLine($"{i} if ({e} == null || depthExceeded_{p.Name}) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
||||
sb.AppendLine($"{i} if ({e} == null) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
||||
|
||||
var elemRefSuffix = p.ElementIsIId ? "IId" : "All";
|
||||
|
||||
|
|
@ -1418,20 +1434,20 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
// Compile-time proven: no ref, no metadata → ZERO branches per element: always Object
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context);");
|
||||
}
|
||||
else if (p.ElementNeedsRefScan && !p.ElementEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i} if (context.WriteObjectRefMarker{elemRefSuffix}()) {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} if (context.WriteObjectRefMarker{elemRefSuffix}()) {writer}.Instance.WriteProperties({e}, context);");
|
||||
}
|
||||
else if (!p.ElementNeedsRefScan && p.ElementEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i} context.WriteObjectMetaMarker({e}, {writer}.s_wrapperSlot);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context);");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{i} if (context.WriteObjectFullMarker{elemRefSuffix}({e}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} if (context.WriteObjectFullMarker{elemRefSuffix}({e}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({e}, context);");
|
||||
}
|
||||
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -1520,13 +1536,11 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
|
||||
// Reference type dictionaries can always be null at runtime regardless of nullable annotation
|
||||
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
||||
sb.AppendLine($"{i}else if (depth > context.MaxDepth) context.WriteByte(BinaryTypeCode.Null);");
|
||||
sb.AppendLine($"{i}else");
|
||||
sb.AppendLine($"{i}{{");
|
||||
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Dictionary);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint){a}.Count);");
|
||||
sb.AppendLine($"{i} var nd_{s} = depth + 2;");
|
||||
sb.AppendLine($"{i} foreach (var kvp_{s} in {a})");
|
||||
sb.AppendLine($"{i} {{");
|
||||
|
||||
|
|
@ -1549,7 +1563,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{ii}AcBinarySerializer.WriteValueGenerated({k}, typeof({keyType}), context, nd_{s});");
|
||||
sb.AppendLine($"{ii}AcBinarySerializer.WriteValueGenerated({k}, typeof({keyType}), context);");
|
||||
}
|
||||
|
||||
// Write value
|
||||
|
|
@ -1577,7 +1591,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
else
|
||||
{
|
||||
// Fallback for non-inlineable value types
|
||||
sb.AppendLine($"{ii}AcBinarySerializer.WriteValueGenerated({v}, typeof({valType}), context, nd_{s});");
|
||||
sb.AppendLine($"{ii}AcBinarySerializer.WriteValueGenerated({v}, typeof({valType}), context);");
|
||||
}
|
||||
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -1593,26 +1607,25 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
var writer = p.DictValueWriterClassName!;
|
||||
|
||||
sb.AppendLine($"{i}if ({v} == null) {{ context.WriteByte(BinaryTypeCode.Null); }}");
|
||||
sb.AppendLine($"{i}else if (nd_{s} > context.MaxDepth) {{ context.WriteByte(BinaryTypeCode.Null); }}");
|
||||
|
||||
var dvRefSuffix = p.DictValueIsIId ? "IId" : "All";
|
||||
|
||||
if (!p.DictValueNeedsRefScan && !p.DictValueEnableMetadata)
|
||||
{
|
||||
// No ref, no metadata → always Object
|
||||
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Object); {writer}.Instance.WriteProperties({v}, context, nd_{s}); }}");
|
||||
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Object); {writer}.Instance.WriteProperties({v}, context); }}");
|
||||
}
|
||||
else if (p.DictValueNeedsRefScan && !p.DictValueEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectRefMarker{dvRefSuffix}()) {writer}.Instance.WriteProperties({v}, context, nd_{s});");
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectRefMarker{dvRefSuffix}()) {writer}.Instance.WriteProperties({v}, context);");
|
||||
}
|
||||
else if (!p.DictValueNeedsRefScan && p.DictValueEnableMetadata)
|
||||
{
|
||||
sb.AppendLine($"{i}else {{ context.WriteObjectMetaMarker({v}, {writer}.s_wrapperSlot); {writer}.Instance.WriteProperties({v}, context, nd_{s}); }}");
|
||||
sb.AppendLine($"{i}else {{ context.WriteObjectMetaMarker({v}, {writer}.s_wrapperSlot); {writer}.Instance.WriteProperties({v}, context); }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectFullMarker{dvRefSuffix}({v}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({v}, context, nd_{s});");
|
||||
sb.AppendLine($"{i}else if (context.WriteObjectFullMarker{dvRefSuffix}({v}, {writer}.s_wrapperSlot)) {writer}.Instance.WriteProperties({v}, context);");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1700,7 +1713,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.DateTimeOffset); context.WriteDateTimeOffsetBits({a});");
|
||||
break;
|
||||
default:
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, typeof({typeName}), context, depth);");
|
||||
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, typeof({typeName}), context);");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1746,7 +1759,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine();
|
||||
|
||||
// ReadProperties — reads all properties into an existing instance (mirrors WriteProperties)
|
||||
sb.AppendLine(" public void ReadProperties<TInput>(object value, AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, int depth)");
|
||||
// No depth safety net on deserialize: wire format is linear + finite, the serializer-side counter
|
||||
// already prevents pathological depth in well-formed payloads.
|
||||
sb.AppendLine(" public void ReadProperties<TInput>(object value, AcBinaryDeserializer.BinaryDeserializationContext<TInput> context)");
|
||||
sb.AppendLine(" where TInput : struct, IBinaryInputBase");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" var obj = Unsafe.As<{ci.FullTypeName}>(value);");
|
||||
|
|
@ -1762,13 +1777,13 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine();
|
||||
|
||||
// ReadObject — IGeneratedBinaryReader implementation (delegates to ReadProperties)
|
||||
sb.AppendLine(" public object? ReadObject<TInput>(AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, int depth, int cacheIndex)");
|
||||
sb.AppendLine(" public object? ReadObject<TInput>(AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, 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<TInput>(obj, context, depth);");
|
||||
sb.AppendLine(" ReadProperties<TInput>(obj, context);");
|
||||
sb.AppendLine(" return obj;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
|
@ -1856,9 +1871,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
// Unknown markered type (char, sbyte, etc.) — rewind + runtime fallback
|
||||
sb.AppendLine($"{i} context._position--;");
|
||||
if (p.IsNullable)
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1);");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}));");
|
||||
else
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1)!;");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}))!;");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2013,20 +2028,19 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}else");
|
||||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} context._position--;");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1);");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}));");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{i}context._position--;");
|
||||
sb.AppendLine($"{i}{a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1)!;");
|
||||
sb.AppendLine($"{i}{a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}))!;");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var reader = p.WriterClassName!.Replace("_GeneratedWriter", "_GeneratedReader");
|
||||
var cast = $"({p.TypeNameForTypeof})";
|
||||
var nd = "depth + 1";
|
||||
|
||||
if (!p.ChildNeedsRefScan)
|
||||
{
|
||||
|
|
@ -2041,7 +2055,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} if ({tc} < BinaryTypeCode.Object) {{ context.GetWrapper(typeof({p.TypeNameForTypeof}), {tc}); if ({tc} >= context._nextRuntimeSlot) context._nextRuntimeSlot = {tc} + 1; }}");
|
||||
sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context);");
|
||||
sb.AppendLine($"{i} {a} = rc_{p.Name};");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
|
|
@ -2051,7 +2065,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} if ({tc} < BinaryTypeCode.Object) {{ context.GetWrapper(typeof({p.TypeNameForTypeof}), {tc}); if ({tc} >= context._nextRuntimeSlot) context._nextRuntimeSlot = {tc} + 1; }}");
|
||||
sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context);");
|
||||
sb.AppendLine($"{i} {a} = rc_{p.Name};");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
|
|
@ -2069,7 +2083,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} case BinaryTypeCode.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} {reader}.Instance.ReadProperties(rc_{p.Name}, context);");
|
||||
sb.AppendLine($"{i} {a} = rc_{p.Name};");
|
||||
sb.AppendLine($"{i} break;");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -2078,7 +2092,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
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} {reader}.Instance.ReadProperties(rc_{p.Name}, context);");
|
||||
sb.AppendLine($"{i} {a} = rc_{p.Name};");
|
||||
sb.AppendLine($"{i} break;");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -2095,7 +2109,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} context.GetWrapper(typeof({p.TypeNameForTypeof}), {tc});");
|
||||
sb.AppendLine($"{i} if ({tc} >= context._nextRuntimeSlot) context._nextRuntimeSlot = {tc} + 1;");
|
||||
sb.AppendLine($"{i} var rc_{p.Name} = new {p.TypeNameForTypeof}();");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context, {nd});");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(rc_{p.Name}, context);");
|
||||
sb.AppendLine($"{i} {a} = rc_{p.Name};");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} break;");
|
||||
|
|
@ -2136,13 +2150,13 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}else");
|
||||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} context._position--;");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1);");
|
||||
sb.AppendLine($"{i} {a} = ({p.TypeNameForTypeof}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}));");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{i}context._position--;");
|
||||
sb.AppendLine($"{i}{a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}), depth + 1)!;");
|
||||
sb.AppendLine($"{i}{a} = ({p.TypeNameForTypeof})AcBinaryDeserializer.ReadValueGenerated(context, typeof({p.TypeNameForTypeof}))!;");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2176,7 +2190,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} var cnt_{s} = (int)context.ReadVarUInt();");
|
||||
sb.AppendLine($"{i} var dict_{s} = new System.Collections.Generic.Dictionary<{keyType}, {valType}>(cnt_{s});");
|
||||
sb.AppendLine($"{i} var nd_{s} = depth + 1;");
|
||||
sb.AppendLine($"{i} for (var di_{s} = 0; di_{s} < cnt_{s}; di_{s}++)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
|
||||
|
|
@ -2184,7 +2197,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
if (canInlineKey)
|
||||
EmitReadDictElement(sb, p.DictKeyKind, keyType, $"dk_{s}", s, i + " ", null, false);
|
||||
else
|
||||
sb.AppendLine($"{i} var dk_{s} = ({keyType})AcBinaryDeserializer.ReadValueGenerated(context, typeof({keyType}), nd_{s})!;");
|
||||
sb.AppendLine($"{i} var dk_{s} = ({keyType})AcBinaryDeserializer.ReadValueGenerated(context, typeof({keyType}))!;");
|
||||
|
||||
// Read value
|
||||
if (p.DictValueKind == PropertyTypeKind.Complex && p.DictValueHasGeneratedWriter)
|
||||
|
|
@ -2196,7 +2209,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} if ({vtc} == BinaryTypeCode.Object)");
|
||||
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} {valReader}.Instance.ReadProperties(rv_{s}, context);");
|
||||
sb.AppendLine($"{i} dv_{s} = rv_{s};");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else if ({vtc} == BinaryTypeCode.ObjectRefFirst)");
|
||||
|
|
@ -2204,7 +2217,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
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} {valReader}.Instance.ReadProperties(rv_{s}, context);");
|
||||
sb.AppendLine($"{i} dv_{s} = rv_{s};");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else if ({vtc} == BinaryTypeCode.ObjectRef)");
|
||||
|
|
@ -2212,13 +2225,13 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} else if ({vtc} != BinaryTypeCode.Null)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context._position--;");
|
||||
sb.AppendLine($"{i} dv_{s} = ({valType}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({valType}), nd_{s});");
|
||||
sb.AppendLine($"{i} dv_{s} = ({valType}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({valType}));");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
else if (canInlineValue)
|
||||
EmitReadDictElement(sb, p.DictValueKind, valType, $"dv_{s}", s, i + " ", null, true);
|
||||
else
|
||||
sb.AppendLine($"{i} var dv_{s} = ({valType}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({valType}), nd_{s});");
|
||||
sb.AppendLine($"{i} var dv_{s} = ({valType}?)AcBinaryDeserializer.ReadValueGenerated(context, typeof({valType}));");
|
||||
|
||||
// Add to dictionary
|
||||
sb.AppendLine($"{i} if (dk_{s} != null) dict_{s}[dk_{s}] = dv_{s}!;");
|
||||
|
|
@ -2351,8 +2364,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
|
||||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} var cnt_{s} = (int)context.ReadVarUInt();");
|
||||
if (isComplexElement)
|
||||
sb.AppendLine($"{i} var nd_{s} = depth + 1;");
|
||||
|
||||
// Create collection + loop based on kind
|
||||
if (p.CollectionKind == "Array")
|
||||
|
|
@ -2420,7 +2431,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} if ({etc} < BinaryTypeCode.Object) {{ context.GetWrapper(typeof({elemTypeName}), {etc}); if ({etc} >= context._nextRuntimeSlot) context._nextRuntimeSlot = {etc} + 1; }}");
|
||||
sb.AppendLine($"{i} var re_{propSuffix} = new {elemTypeName}();");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context, nd_{propSuffix});");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context);");
|
||||
sb.AppendLine($"{i} {assignExpr}");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
|
|
@ -2434,7 +2445,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} case BinaryTypeCode.Object:");
|
||||
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} {reader}.Instance.ReadProperties(re_{propSuffix}, context);");
|
||||
sb.AppendLine($"{i} {assignExpr}");
|
||||
sb.AppendLine($"{i} break;");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -2443,7 +2454,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
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} {reader}.Instance.ReadProperties(re_{propSuffix}, context);");
|
||||
sb.AppendLine($"{i} {assignExpr}");
|
||||
sb.AppendLine($"{i} break;");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
|
@ -2464,7 +2475,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} context.GetWrapper(typeof({elemTypeName}), {etc});");
|
||||
sb.AppendLine($"{i} if ({etc} >= context._nextRuntimeSlot) context._nextRuntimeSlot = {etc} + 1;");
|
||||
sb.AppendLine($"{i} var re_{propSuffix} = new {elemTypeName}();");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context, nd_{propSuffix});");
|
||||
sb.AppendLine($"{i} {reader}.Instance.ReadProperties(re_{propSuffix}, context);");
|
||||
sb.AppendLine($"{i} {assignExpr}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} break;");
|
||||
|
|
|
|||
|
|
@ -21,22 +21,19 @@ internal sealed class TestOrderWriter : IGeneratedBinaryWriter
|
|||
{
|
||||
internal static readonly TestOrderWriter Instance = new();
|
||||
|
||||
public void WriteProperties<TOutput>(
|
||||
object value,
|
||||
AcBinarySerializer.BinarySerializationContext<TOutput> context,
|
||||
int depth)
|
||||
public void WriteProperties<TOutput>(object value,
|
||||
AcBinarySerializer.BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var obj = Unsafe.As<TestOrder_All_True>(value);
|
||||
var nextDepth = depth;
|
||||
|
||||
// Properties in alphabetical order (matching runtime serializer):
|
||||
|
||||
// AuditMetadata: MetadataInfo_All_True? (complex, nullable)
|
||||
WriteComplexOrNull(obj.AuditMetadata, context, nextDepth);
|
||||
WriteComplexOrNull(obj.AuditMetadata, context);
|
||||
|
||||
// Category: SharedCategory_All_True? (complex, nullable)
|
||||
WriteComplexOrNull(obj.Category, context, nextDepth);
|
||||
WriteComplexOrNull(obj.Category, context);
|
||||
|
||||
// CreatedAt: DateTime (markerless)
|
||||
context.WriteDateTimeBits(obj.CreatedAt);
|
||||
|
|
@ -45,22 +42,22 @@ internal sealed class TestOrderWriter : IGeneratedBinaryWriter
|
|||
context.WriteVarInt(obj.Id);
|
||||
|
||||
// Items: List<TestOrderItem_All_True> (collection)
|
||||
WriteComplexOrNull(obj.Items, context, nextDepth);
|
||||
WriteComplexOrNull(obj.Items, context);
|
||||
|
||||
// MetadataList: List<MetadataInfo_All_True> (collection)
|
||||
WriteComplexOrNull(obj.MetadataList, context, nextDepth);
|
||||
WriteComplexOrNull(obj.MetadataList, context);
|
||||
|
||||
// NoMergeItems: List<TestOrderItem_All_True> (collection)
|
||||
WriteComplexOrNull(obj.NoMergeItems, context, nextDepth);
|
||||
WriteComplexOrNull(obj.NoMergeItems, context);
|
||||
|
||||
// OrderMetadata: MetadataInfo_All_True? (complex, nullable)
|
||||
WriteComplexOrNull(obj.OrderMetadata, context, nextDepth);
|
||||
WriteComplexOrNull(obj.OrderMetadata, context);
|
||||
|
||||
// OrderNumber: string
|
||||
AcBinarySerializer.WriteStringGenerated(obj.OrderNumber, context);
|
||||
|
||||
// Owner: SharedUser? (complex, nullable)
|
||||
WriteComplexOrNull(obj.Owner, context, nextDepth);
|
||||
WriteComplexOrNull(obj.Owner, context);
|
||||
|
||||
// PaidDateUtc: DateTime? (nullable)
|
||||
var paidDate = obj.PaidDateUtc;
|
||||
|
|
@ -75,22 +72,22 @@ internal sealed class TestOrderWriter : IGeneratedBinaryWriter
|
|||
}
|
||||
|
||||
// PrimaryTag: SharedTag_All_True? (complex, nullable)
|
||||
WriteComplexOrNull(obj.PrimaryTag, context, nextDepth);
|
||||
WriteComplexOrNull(obj.PrimaryTag, context);
|
||||
|
||||
// SecondaryTag: SharedTag_All_True? (complex, nullable)
|
||||
WriteComplexOrNull(obj.SecondaryTag, context, nextDepth);
|
||||
WriteComplexOrNull(obj.SecondaryTag, context);
|
||||
|
||||
// Status: TestStatus (enum, markerless)
|
||||
context.WriteVarInt((int)obj.Status);
|
||||
|
||||
// Tags: List<SharedTag_All_True> (collection)
|
||||
WriteComplexOrNull(obj.Tags, context, nextDepth);
|
||||
WriteComplexOrNull(obj.Tags, context);
|
||||
|
||||
// TotalAmount: decimal (markerless)
|
||||
context.WriteDecimalBits(obj.TotalAmount);
|
||||
}
|
||||
|
||||
public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth) where TOutput : struct, IBinaryOutputBase
|
||||
public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
@ -98,12 +95,12 @@ internal sealed class TestOrderWriter : IGeneratedBinaryWriter
|
|||
public void ScanForDuplicates<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (!context.HasCaching) return;
|
||||
ScanObject(value, context, 0);
|
||||
ScanObject(value, context);
|
||||
context.SortWritePlan();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteComplexOrNull<TOutput>(object? value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteComplexOrNull<TOutput>(object? value, AcBinarySerializer.BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (value == null)
|
||||
|
|
@ -112,6 +109,6 @@ internal sealed class TestOrderWriter : IGeneratedBinaryWriter
|
|||
return;
|
||||
}
|
||||
|
||||
AcBinarySerializer.WriteValueGenerated(value, value.GetType(), context, depth);
|
||||
AcBinarySerializer.WriteValueGenerated(value, value.GetType(), context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public abstract class AcSerializerContextBase<TMetadata, TOptions>
|
|||
public TOptions Options { get; private set; } = null!;
|
||||
|
||||
public byte MaxDepth => Options.MaxDepth;
|
||||
|
||||
public ReferenceHandlingMode ReferenceHandling => Options.ReferenceHandling;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ public static partial class AcBinaryDeserializer
|
|||
BinaryTypeCode.Null => null,
|
||||
BinaryTypeCode.Object => ReadObjectWithMapping(context, destType, indexMapping, depth, registerInCache: false),
|
||||
BinaryTypeCode.ObjectRefFirst => ReadObjectWithMapping(context, destType, indexMapping, depth, registerInCache: true),
|
||||
_ => ReadValue(context, destType, depth) // Primitives, arrays, etc. use normal path
|
||||
_ => ReadValue(context, destType) // Primitives, arrays, etc. use normal path
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -329,7 +329,7 @@ public static partial class AcBinaryDeserializer
|
|||
if (TryReadAndSetTypedValue(context, target, propInfo, peekCode))
|
||||
return;
|
||||
|
||||
var value = ReadValue(context, propInfo.PropertyType, nextDepth);
|
||||
var value = ReadValue(context, propInfo.PropertyType);
|
||||
propInfo.SetValue(target, value);
|
||||
}
|
||||
catch (InvalidCastException ex)
|
||||
|
|
|
|||
|
|
@ -100,14 +100,12 @@ public static partial class AcBinaryDeserializer
|
|||
/// UseMetadata=true: cacheMap[i] gives the setter (null → skip).
|
||||
/// UseMetadata=false: properties[i] gives the setter directly.
|
||||
/// </summary>
|
||||
private static void PopulateObjectPropertiesIndexed<TInput>(
|
||||
BinaryDeserializationContext<TInput> context,
|
||||
object target,
|
||||
TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper,
|
||||
int depth,
|
||||
bool skipDefaultWrite)
|
||||
private static void PopulateObjectPropertiesIndexed<TInput>(BinaryDeserializationContext<TInput> context, object target, TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, int depth, bool skipDefaultWrite)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
// No depth safety net on deserialize: wire format is linear + finite, the serializer-side counter
|
||||
// already prevents pathological depth in well-formed payloads. Malicious wire is out of scope.
|
||||
|
||||
var metadata = wrapper.Metadata;
|
||||
var properties = metadata.PropertiesArray;
|
||||
var cacheMap = wrapper.CacheMap;
|
||||
|
|
@ -246,7 +244,7 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
// Marker already consumed → rewind so ReadValue can read it
|
||||
context._position--;
|
||||
var nestedValue = ReadValue(context, propInfo.PropertyType, nextDepth);
|
||||
var nestedValue = ReadValue(context, propInfo.PropertyType);
|
||||
if (nestedValue != null)
|
||||
{
|
||||
var complexIdx = propInfo.ComplexPropertyIndex;
|
||||
|
|
@ -276,14 +274,14 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
// Marker already consumed — go straight to ReadObjectCoreWithWrapper
|
||||
var propWrapper = ResolvePropertyWrapper(state.ParentWrapper, complexIdx, propInfo.PropertyType, context);
|
||||
var value = ReadObjectCoreWithWrapper(context, propWrapper, nextDepth, cacheIndex: -1);
|
||||
var value = ReadObjectCoreWithWrapper(context, propWrapper, cacheIndex: -1);
|
||||
propInfo.SetValue(target, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Marker already consumed → rewind so ReadValue can read it
|
||||
context._position--;
|
||||
var value = ReadValue(context, propInfo.PropertyType, nextDepth);
|
||||
var value = ReadValue(context, propInfo.PropertyType);
|
||||
propInfo.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -365,10 +363,12 @@ public static partial class AcBinaryDeserializer
|
|||
/// Called from ReadObject/ReadObjectWithMetadata for new instances.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void PopulateObject<TInput>(BinaryDeserializationContext<TInput> context, object target, TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, int depth, bool skipDefaultWrite)
|
||||
private static void PopulateObject<TInput>(BinaryDeserializationContext<TInput> context, object target, TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, bool skipDefaultWrite)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
PopulateObjectPropertiesIndexed(context, target, wrapper, depth, skipDefaultWrite);
|
||||
// Bridge: PopulateObjectPropertiesIndexed still has int depth in signature (other callers use it).
|
||||
// Pass 0 as placeholder; depth-cleanup of PopulateObjectPropertiesIndexed + its other callers is pending.
|
||||
PopulateObjectPropertiesIndexed(context, target, wrapper, 0, skipDefaultWrite);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -393,7 +393,7 @@ public static partial class AcBinaryDeserializer
|
|||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// ReadValue handles ChainMode internally (ReadObject returns cached instance)
|
||||
var value = ReadValue(context, elementType, nextDepth);
|
||||
var value = ReadValue(context, elementType);
|
||||
targetList.Add(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -461,13 +461,13 @@ public static partial class AcBinaryDeserializer
|
|||
object? value;
|
||||
if (typeCode == BinaryTypeCode.Object && elementMetadata != null)
|
||||
{
|
||||
value = ReadObjectCoreWithWrapper(context, wrapper, nextDepth, cacheIndex: cacheIndex);
|
||||
value = ReadObjectCoreWithWrapper(context, wrapper, cacheIndex: cacheIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Marker already consumed → rewind so ReadValue can read it
|
||||
context._position--;
|
||||
value = ReadValue(context, elementType, nextDepth);
|
||||
value = ReadValue(context, elementType);
|
||||
}
|
||||
|
||||
if (i < existingCount)
|
||||
|
|
@ -558,7 +558,7 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
// Marker already consumed → rewind so ReadValue can read it
|
||||
context._position--;
|
||||
newItem = ReadValue(context, elementType, nextDepth);
|
||||
newItem = ReadValue(context, elementType);
|
||||
if (newItem == null) continue;
|
||||
}
|
||||
|
||||
|
|
@ -667,7 +667,7 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
// Marker already consumed → rewind so ReadValue can read it
|
||||
context._position--;
|
||||
newItem = ReadValue(context, elementType, nextDepth);
|
||||
newItem = ReadValue(context, elementType);
|
||||
if (newItem == null) continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public static partial class AcBinaryDeserializer
|
|||
private static Dictionary<Type, TypeConversionInfo>? t_typeConversionLocalCache;
|
||||
|
||||
// Type dispatch table for fast ReadValue — generic per TInput, JIT specializes each
|
||||
private delegate object? TypeReader<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private delegate object? TypeReader<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase;
|
||||
|
||||
private static class TypeReaderTable<TInput> where TInput : struct, IBinaryInputBase
|
||||
|
|
@ -82,37 +82,37 @@ public static partial class AcBinaryDeserializer
|
|||
private static TypeReader<TInput>?[] InitReaders()
|
||||
{
|
||||
var readers = new TypeReader<TInput>?[byte.MaxValue + 1];
|
||||
readers[BinaryTypeCode.Null] = static (_, _, _) => null;
|
||||
readers[BinaryTypeCode.True] = static (_, _, _) => true;
|
||||
readers[BinaryTypeCode.False] = static (_, _, _) => false;
|
||||
readers[BinaryTypeCode.Int8] = static (ctx, _, _) => (sbyte)ctx.ReadByte();
|
||||
readers[BinaryTypeCode.UInt8] = static (ctx, _, _) => ctx.ReadByte();
|
||||
readers[BinaryTypeCode.Int16] = static (ctx, _, _) => ctx.ReadInt16Unsafe();
|
||||
readers[BinaryTypeCode.UInt16] = static (ctx, _, _) => ctx.ReadUInt16Unsafe();
|
||||
readers[BinaryTypeCode.Int32] = static (ctx, type, _) => ReadInt32Value(ctx, type);
|
||||
readers[BinaryTypeCode.UInt32] = static (ctx, _, _) => ctx.ReadVarUInt();
|
||||
readers[BinaryTypeCode.Int64] = static (ctx, _, _) => ctx.ReadVarLong();
|
||||
readers[BinaryTypeCode.UInt64] = static (ctx, _, _) => ctx.ReadVarULong();
|
||||
readers[BinaryTypeCode.Float32] = static (ctx, _, _) => ctx.ReadSingleUnsafe();
|
||||
readers[BinaryTypeCode.Float64] = static (ctx, _, _) => ctx.ReadDoubleUnsafe();
|
||||
readers[BinaryTypeCode.Decimal] = static (ctx, _, _) => ctx.ReadDecimalUnsafe();
|
||||
readers[BinaryTypeCode.Char] = static (ctx, _, _) => ctx.ReadCharUnsafe();
|
||||
readers[BinaryTypeCode.Null] = static (_, _) => null;
|
||||
readers[BinaryTypeCode.True] = static (_, _) => true;
|
||||
readers[BinaryTypeCode.False] = static (_, _) => false;
|
||||
readers[BinaryTypeCode.Int8] = static (ctx, _) => (sbyte)ctx.ReadByte();
|
||||
readers[BinaryTypeCode.UInt8] = static (ctx, _) => ctx.ReadByte();
|
||||
readers[BinaryTypeCode.Int16] = static (ctx, _) => ctx.ReadInt16Unsafe();
|
||||
readers[BinaryTypeCode.UInt16] = static (ctx, _) => ctx.ReadUInt16Unsafe();
|
||||
readers[BinaryTypeCode.Int32] = static (ctx, type) => ReadInt32Value(ctx, type);
|
||||
readers[BinaryTypeCode.UInt32] = static (ctx, _) => ctx.ReadVarUInt();
|
||||
readers[BinaryTypeCode.Int64] = static (ctx, _) => ctx.ReadVarLong();
|
||||
readers[BinaryTypeCode.UInt64] = static (ctx, _) => ctx.ReadVarULong();
|
||||
readers[BinaryTypeCode.Float32] = static (ctx, _) => ctx.ReadSingleUnsafe();
|
||||
readers[BinaryTypeCode.Float64] = static (ctx, _) => ctx.ReadDoubleUnsafe();
|
||||
readers[BinaryTypeCode.Decimal] = static (ctx, _) => ctx.ReadDecimalUnsafe();
|
||||
readers[BinaryTypeCode.Char] = static (ctx, _) => ctx.ReadCharUnsafe();
|
||||
// H2Q6 non-ASCII tier readers (Compact mode): fixed-width header [charLen][utf8Len] + 1-pass decode.
|
||||
// FastWire mode dispatches the StringSmall (=91) marker through the same handler — see ReadStringSmall.
|
||||
readers[BinaryTypeCode.StringSmall] = static (ctx, _, _) => ReadStringSmall(ctx);
|
||||
readers[BinaryTypeCode.StringMedium] = static (ctx, _, _) => ReadStringMedium(ctx);
|
||||
readers[BinaryTypeCode.StringBig] = static (ctx, _, _) => ReadStringBig(ctx);
|
||||
readers[BinaryTypeCode.StringInterned] = static (ctx, _, _) => ctx.GetInternedString((int)ctx.ReadVarUInt());
|
||||
readers[BinaryTypeCode.StringEmpty] = static (_, _, _) => string.Empty;
|
||||
readers[BinaryTypeCode.StringSmall] = static (ctx, _) => ReadStringSmall(ctx);
|
||||
readers[BinaryTypeCode.StringMedium] = static (ctx, _) => ReadStringMedium(ctx);
|
||||
readers[BinaryTypeCode.StringBig] = static (ctx, _) => ReadStringBig(ctx);
|
||||
readers[BinaryTypeCode.StringInterned] = static (ctx, _) => ctx.GetInternedString((int)ctx.ReadVarUInt());
|
||||
readers[BinaryTypeCode.StringEmpty] = static (_, _) => string.Empty;
|
||||
// H2Q6 interning tier readers (Compact mode only — Big tier never engages on interning path)
|
||||
readers[BinaryTypeCode.StringInternFirstSmall] = static (ctx, _, _) => ReadAndRegisterInternedStringSmall(ctx);
|
||||
readers[BinaryTypeCode.StringInternFirstMedium] = static (ctx, _, _) => ReadAndRegisterInternedStringMedium(ctx);
|
||||
readers[BinaryTypeCode.StringAscii] = static (ctx, _, _) => ReadPlainStringAscii(ctx);
|
||||
readers[BinaryTypeCode.DateTime] = static (ctx, _, _) => ctx.ReadDateTimeUnsafe();
|
||||
readers[BinaryTypeCode.DateTimeOffset] = static (ctx, _, _) => ctx.ReadDateTimeOffsetUnsafe();
|
||||
readers[BinaryTypeCode.TimeSpan] = static (ctx, _, _) => ctx.ReadTimeSpanUnsafe();
|
||||
readers[BinaryTypeCode.Guid] = static (ctx, _, _) => ctx.ReadGuidUnsafe();
|
||||
readers[BinaryTypeCode.Enum] = static (ctx, type, _) => ReadEnumValue(ctx, type);
|
||||
readers[BinaryTypeCode.StringInternFirstSmall] = static (ctx, _) => ReadAndRegisterInternedStringSmall(ctx);
|
||||
readers[BinaryTypeCode.StringInternFirstMedium] = static (ctx, _) => ReadAndRegisterInternedStringMedium(ctx);
|
||||
readers[BinaryTypeCode.StringAscii] = static (ctx, _) => ReadPlainStringAscii(ctx);
|
||||
readers[BinaryTypeCode.DateTime] = static (ctx, _) => ctx.ReadDateTimeUnsafe();
|
||||
readers[BinaryTypeCode.DateTimeOffset] = static (ctx, _) => ctx.ReadDateTimeOffsetUnsafe();
|
||||
readers[BinaryTypeCode.TimeSpan] = static (ctx, _) => ctx.ReadTimeSpanUnsafe();
|
||||
readers[BinaryTypeCode.Guid] = static (ctx, _) => ctx.ReadGuidUnsafe();
|
||||
readers[BinaryTypeCode.Enum] = static (ctx, type) => ReadEnumValue(ctx, type);
|
||||
readers[BinaryTypeCode.Object] = ReadObject;
|
||||
readers[BinaryTypeCode.ObjectRefFirst] = ReadObjectRefFirst;
|
||||
readers[BinaryTypeCode.ObjectWithMetadata] = ReadObjectWithMetadata;
|
||||
|
|
@ -124,7 +124,7 @@ public static partial class AcBinaryDeserializer
|
|||
readers[BinaryTypeCode.ObjectWithTypeIndexRefFirst] = ReadObjectWithTypeIndexRefFirst;
|
||||
readers[BinaryTypeCode.Array] = ReadArray;
|
||||
readers[BinaryTypeCode.Dictionary] = ReadDictionary;
|
||||
readers[BinaryTypeCode.ByteArray] = static (ctx, _, _) => ReadByteArray(ctx);
|
||||
readers[BinaryTypeCode.ByteArray] = static (ctx, _) => ReadByteArray(ctx);
|
||||
|
||||
// V4N5 cleanup (2026-05-06): FixStr (UTF-8 short non-ASCII, 103..134) range REMOVED.
|
||||
// Non-ASCII short strings now use StringSmall tier marker (registered above).
|
||||
|
|
@ -155,9 +155,9 @@ public static partial class AcBinaryDeserializer
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static TypeReader<TInput> CreateFixStrAsciiReader<TInput>(int length) where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
if (length == 0) return static (_, _, _) => string.Empty;
|
||||
if (length == 0) return static (_, _) => string.Empty;
|
||||
|
||||
return (ctx, _, _) => ctx.ReadAsciiBytesAsString(length);
|
||||
return (ctx, _) => ctx.ReadAsciiBytesAsString(length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -166,7 +166,7 @@ public static partial class AcBinaryDeserializer
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static TypeReader<TInput> CreateFixObjReader<TInput>(int slot) where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
return (ctx, targetType, depth) => ReadObjectFromSlot(ctx, slot, targetType, depth);
|
||||
return (ctx, targetType) => ReadObjectFromSlot(ctx, slot, targetType);
|
||||
}
|
||||
|
||||
//private static readonly Encoding Utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
|
||||
|
|
@ -433,7 +433,7 @@ public static partial class AcBinaryDeserializer
|
|||
try
|
||||
{
|
||||
context.ReadHeader();
|
||||
return ReadValue(context, targetType, 0);
|
||||
return ReadValue(context, targetType);
|
||||
}
|
||||
catch (AcBinaryDeserializationException) { throw; }
|
||||
catch (Exception ex)
|
||||
|
|
@ -1084,16 +1084,16 @@ public static partial class AcBinaryDeserializer
|
|||
/// Reads typeCode + dispatches via TypeReaderTable — same as runtime ReadValue.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static object? ReadValueGenerated<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
internal static object? ReadValueGenerated<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
return ReadValue(context, targetType, depth);
|
||||
return ReadValue(context, targetType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimized value reader using FrozenDictionary dispatch table.
|
||||
/// </summary>
|
||||
private static object? ReadValue<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadValue<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
if (context.IsAtEnd) return null;
|
||||
|
|
@ -1131,7 +1131,7 @@ public static partial class AcBinaryDeserializer
|
|||
var reader = TypeReaderTable<TInput>.Readers[typeCode];
|
||||
if (reader != null)
|
||||
{
|
||||
return reader(context, targetType, depth);
|
||||
return reader(context, targetType);
|
||||
}
|
||||
|
||||
throw new AcBinaryDeserializationException(
|
||||
|
|
@ -1439,7 +1439,7 @@ public static partial class AcBinaryDeserializer
|
|||
/// ami ritka eset (2+ referencia), tehát nem lassítja a hot path-ot.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObjectRef<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectRef<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var cacheIndex = (int)context.ReadVarUInt();
|
||||
|
|
@ -1452,12 +1452,7 @@ public static partial class AcBinaryDeserializer
|
|||
/// Subsequent: direct array access (~1-2ns).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObjectFromSlot<TInput>(
|
||||
BinaryDeserializationContext<TInput> context,
|
||||
int slot,
|
||||
Type targetType,
|
||||
int depth)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
private static object? ReadObjectFromSlot<TInput>(BinaryDeserializationContext<TInput> context, int slot, Type targetType) where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var wrapper = context.GetWrapper(targetType, slot);
|
||||
|
||||
|
|
@ -1470,20 +1465,20 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
var generatedReader = wrapper.GeneratedReader;
|
||||
if (generatedReader != null)
|
||||
return generatedReader.ReadObject(context, depth, cacheIndex: -1);
|
||||
return generatedReader.ReadObject(context, cacheIndex: -1);
|
||||
}
|
||||
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, depth, cacheIndex: -1);
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, cacheIndex: -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Object olvasása (nem tracked, vagy UseMetadata nélkül).
|
||||
/// Wire format: [Object][props...]
|
||||
/// </summary>
|
||||
private static object? ReadObject<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObject<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
return ReadObjectCore(context, targetType, depth, cacheIndex: -1);
|
||||
return ReadObjectCore(context, targetType, cacheIndex: -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1491,11 +1486,11 @@ public static partial class AcBinaryDeserializer
|
|||
/// Wire format: [ObjectRefFirst][VarUInt cacheIndex][props...]
|
||||
/// Az objektumot regisztráljuk a cache-be a megadott index-re.
|
||||
/// </summary>
|
||||
private static object? ReadObjectRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var cacheIndex = (int)context.ReadVarUInt();
|
||||
return ReadObjectCore(context, targetType, depth, cacheIndex: cacheIndex);
|
||||
return ReadObjectCore(context, targetType, cacheIndex: cacheIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1504,7 +1499,7 @@ public static partial class AcBinaryDeserializer
|
|||
/// Reads the runtime type name, resolves it, registers wrapper in poly slot cache,
|
||||
/// then reads the inner marker via ReadValue.
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithTypeName<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithTypeName<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var typeName = ReadPlainString(context);
|
||||
|
|
@ -1515,7 +1510,7 @@ public static partial class AcBinaryDeserializer
|
|||
var wrapper = context.GetWrapper(resolvedType);
|
||||
context.RegisterPolymorphicWrapper(wrapper);
|
||||
// Next byte is the actual inner marker (Object/Array/Dict/etc.) — read it via ReadValue
|
||||
return ReadValue(context, resolvedType, depth);
|
||||
return ReadValue(context, resolvedType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1523,7 +1518,7 @@ public static partial class AcBinaryDeserializer
|
|||
/// Wire format: [ObjectWithTypeNameRefFirst (69)] [TypeName string] [VarUInt refCacheIndex] [properties...]
|
||||
/// Object body follows directly — no inner Object/ObjectRefFirst marker.
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithTypeNameRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithTypeNameRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var typeName = ReadPlainString(context);
|
||||
|
|
@ -1534,7 +1529,7 @@ public static partial class AcBinaryDeserializer
|
|||
var wrapper = context.GetWrapper(resolvedType);
|
||||
context.RegisterPolymorphicWrapper(wrapper);
|
||||
var cacheIndex = (int)context.ReadVarUInt();
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, depth, cacheIndex);
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, cacheIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1543,12 +1538,12 @@ public static partial class AcBinaryDeserializer
|
|||
/// Looks up the previously registered wrapper by index (~1-2ns array access),
|
||||
/// then reads the inner marker via ReadValue.
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithTypeIndex<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithTypeIndex<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var typeIndex = (int)context.ReadVarUInt();
|
||||
var wrapper = context.GetPolymorphicWrapper(typeIndex);
|
||||
return ReadValue(context, wrapper.Metadata.SourceType, depth);
|
||||
return ReadValue(context, wrapper.Metadata.SourceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1556,26 +1551,28 @@ public static partial class AcBinaryDeserializer
|
|||
/// Wire format: [ObjectWithTypeIndexRefFirst (71)] [VarUInt typeIndex] [VarUInt refCacheIndex] [properties...]
|
||||
/// Object body follows directly — no inner Object/ObjectRefFirst marker. 0 dictionary lookup.
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithTypeIndexRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithTypeIndexRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var typeIndex = (int)context.ReadVarUInt();
|
||||
var wrapper = context.GetPolymorphicWrapper(typeIndex);
|
||||
var cacheIndex = (int)context.ReadVarUInt();
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, depth, cacheIndex);
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, cacheIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Object olvasás core implementáció.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="cacheIndex">-1 = not cached, 0+ = register at this cache index</param>
|
||||
private static object? ReadObjectCore<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth, int cacheIndex)
|
||||
private static object? ReadObjectCore<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int cacheIndex)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
// Handle dictionary types
|
||||
if (IsDictionaryType(targetType, out var keyType, out var valueType))
|
||||
{
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!, depth);
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!);
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(targetType);
|
||||
|
|
@ -1588,16 +1585,16 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
var generatedReader = wrapper.GeneratedReader;
|
||||
if (generatedReader != null)
|
||||
return generatedReader.ReadObject(context, depth, cacheIndex);
|
||||
return generatedReader.ReadObject(context, cacheIndex);
|
||||
}
|
||||
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, depth, cacheIndex);
|
||||
return ReadObjectCoreWithWrapper(context, wrapper, cacheIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Object olvasás with pre-resolved wrapper (eliminates GetWrapper dictionary lookup).
|
||||
/// </summary>
|
||||
private static object? ReadObjectCoreWithWrapper<TInput>(BinaryDeserializationContext<TInput> context, TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, int depth, int cacheIndex)
|
||||
private static object? ReadObjectCoreWithWrapper<TInput>(BinaryDeserializationContext<TInput> context, TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, int cacheIndex)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var metadata = wrapper.Metadata;
|
||||
|
|
@ -1611,7 +1608,7 @@ public static partial class AcBinaryDeserializer
|
|||
context.RegisterInternedValueAt(cacheIndex, instance);
|
||||
}
|
||||
|
||||
PopulateObject(context, instance, wrapper, depth, skipDefaultWrite: true);
|
||||
PopulateObject(context, instance, wrapper, skipDefaultWrite: true);
|
||||
|
||||
// ChainMode kezelés
|
||||
if (context.IsChainMode && metadata.IsIId && metadata.IdType != null)
|
||||
|
|
@ -1637,10 +1634,10 @@ public static partial class AcBinaryDeserializer
|
|||
/// Első előfordulás: [ObjectWithMetadata][propNameHash (4b)][propCount (VarUInt)][hash0..N][props...]
|
||||
/// Ismételt: [ObjectWithMetadata][propNameHash (4b)][props...]
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithMetadata<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithMetadata<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
return ReadObjectWithMetadataCore(context, targetType, depth, cacheIndex: -1);
|
||||
return ReadObjectWithMetadataCore(context, targetType, cacheIndex: -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1648,18 +1645,20 @@ public static partial class AcBinaryDeserializer
|
|||
/// Wire format: [ObjectWithMetadataRefFirst][VarUInt cacheIndex][propNameHash (4b)][...][props...]
|
||||
/// Az objektumot regisztráljuk a cache-be a megadott index-re.
|
||||
/// </summary>
|
||||
private static object? ReadObjectWithMetadataRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadObjectWithMetadataRefFirst<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var cacheIndex = (int)context.ReadVarUInt();
|
||||
return ReadObjectWithMetadataCore(context, targetType, depth, cacheIndex: cacheIndex);
|
||||
return ReadObjectWithMetadataCore(context, targetType, cacheIndex: cacheIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ObjectWithMetadata olvasás core implementáció.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="cacheIndex">-1 = not cached, 0+ = register at this cache index</param>
|
||||
private static object? ReadObjectWithMetadataCore<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth, int cacheIndex)
|
||||
private static object? ReadObjectWithMetadataCore<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int cacheIndex)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
// Inline metadata: propNameHash mindig jön
|
||||
|
|
@ -1681,7 +1680,7 @@ public static partial class AcBinaryDeserializer
|
|||
// Handle dictionary types
|
||||
if (IsDictionaryType(targetType, out var keyType, out var valueType))
|
||||
{
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!, depth);
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!);
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(targetType);
|
||||
|
|
@ -1699,7 +1698,7 @@ public static partial class AcBinaryDeserializer
|
|||
if (wrapper.CacheMap == null)
|
||||
BuildCacheMap(wrapper, sourceHashes);
|
||||
|
||||
PopulateObject(context, instance, wrapper, depth, skipDefaultWrite: true);
|
||||
PopulateObject(context, instance, wrapper, skipDefaultWrite: true);
|
||||
|
||||
// ChainMode kezelés
|
||||
if (context.IsChainMode && metadata.IsIId && metadata.IdType != null)
|
||||
|
|
@ -1784,14 +1783,13 @@ public static partial class AcBinaryDeserializer
|
|||
|
||||
#region Array Reading
|
||||
|
||||
private static object? ReadArray<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadArray<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var elementType = GetCollectionElementType(targetType);
|
||||
if (elementType == null) elementType = typeof(object);
|
||||
|
||||
var count = (int)context.ReadVarUInt();
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
// Optimized path for primitive arrays
|
||||
if (targetType.IsArray && count > 0)
|
||||
|
|
@ -1805,7 +1803,7 @@ public static partial class AcBinaryDeserializer
|
|||
var array = Array.CreateInstance(elementType, count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var value = ReadValue(context, elementType, nextDepth);
|
||||
var value = ReadValue(context, elementType);
|
||||
array.SetValue(value, i);
|
||||
}
|
||||
|
||||
|
|
@ -1832,7 +1830,7 @@ public static partial class AcBinaryDeserializer
|
|||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var value = ReadValue(context, elementType, nextDepth);
|
||||
var value = ReadValue(context, elementType);
|
||||
list.Add(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -2093,7 +2091,7 @@ public static partial class AcBinaryDeserializer
|
|||
|
||||
#region Dictionary Reading
|
||||
|
||||
private static object? ReadDictionary<TInput>(BinaryDeserializationContext<TInput> context, Type targetType, int depth)
|
||||
private static object? ReadDictionary<TInput>(BinaryDeserializationContext<TInput> context, Type targetType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
if (!IsDictionaryType(targetType, out var keyType, out var valueType))
|
||||
|
|
@ -2102,21 +2100,20 @@ public static partial class AcBinaryDeserializer
|
|||
valueType = typeof(object);
|
||||
}
|
||||
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!, depth);
|
||||
return ReadDictionaryAsObject(context, keyType!, valueType!);
|
||||
}
|
||||
|
||||
private static object ReadDictionaryAsObject<TInput>(BinaryDeserializationContext<TInput> context, Type keyType, Type valueType, int depth)
|
||||
private static object ReadDictionaryAsObject<TInput>(BinaryDeserializationContext<TInput> context, Type keyType, Type valueType)
|
||||
where TInput : struct, IBinaryInputBase
|
||||
{
|
||||
var dictType = DictionaryGenericType.MakeGenericType(keyType, valueType);
|
||||
var count = (int)context.ReadVarUInt();
|
||||
var dict = (IDictionary)Activator.CreateInstance(dictType, count)!;
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var key = ReadValue(context, keyType, nextDepth);
|
||||
var value = ReadValue(context, valueType, nextDepth);
|
||||
var key = ReadValue(context, keyType);
|
||||
var value = ReadValue(context, valueType);
|
||||
if (key != null)
|
||||
dict.Add(key, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,14 @@ public static partial class AcBinarySerializer
|
|||
private int _nextCacheIndex;
|
||||
public int NextFirstIndex; // Next first occurrence index for scan pass. Direct access for performance.
|
||||
|
||||
/// <summary>
|
||||
/// Global recursion depth counter — final safety net against pathological graphs and non-IId cycles.
|
||||
/// Incremented/decremented at object-recursion entry/exit points (WriteObject runtime + generated WriteProperties/ScanObject).
|
||||
/// Checked against <see cref="AcSerializerContextBase{TMetadata,TOptions}.MaxDepth"/> (byte, default 255).
|
||||
/// Replaces the per-call <c>int depth</c> parameter passing — one byte field on context, ~5-7 cycles per object instead of per call.
|
||||
/// </summary>
|
||||
internal byte RecursionDepth;
|
||||
|
||||
#region WriteDuplicateEntry — scan pass output for write pass cursor
|
||||
|
||||
private WriteDuplicateEntry[]? _writePlan;
|
||||
|
|
@ -306,6 +314,7 @@ public static partial class AcBinarySerializer
|
|||
_nextCacheIndex = 0;
|
||||
NextFirstIndex = 0;
|
||||
ScanVisitIndex = 0;
|
||||
RecursionDepth = 0;
|
||||
WritePlanCursor = 0;
|
||||
WriteVisitIndex = 0;
|
||||
_nextWritePlanVisitIndex = int.MaxValue;
|
||||
|
|
|
|||
|
|
@ -31,19 +31,19 @@ public static partial class AcBinarySerializer
|
|||
var genWriter = wrapper.GeneratedWriter;
|
||||
if (genWriter != null && context.Options.UseGeneratedCode)
|
||||
{
|
||||
genWriter.ScanObject(value, context, 0);
|
||||
genWriter.ScanObject(value, context);
|
||||
context.SortWritePlan();
|
||||
return;
|
||||
}
|
||||
|
||||
ScanValue(value, wrapper, context, 0);
|
||||
ScanValue(value, wrapper, context);
|
||||
context.SortWritePlan();
|
||||
}
|
||||
|
||||
private static void ScanValue<TOutput>(object? value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void ScanValue<TOutput>(object? value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (value == null || depth > context.MaxDepth)
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
var metadata = wrapper.Metadata;
|
||||
|
|
@ -64,8 +64,6 @@ public static partial class AcBinarySerializer
|
|||
if (metadata.ElementNeedsScan &&
|
||||
!((isStringCollectionElementType = ReferenceEquals(metadata.CollectionElementType, StringType)) && !context.UseStringInterning))
|
||||
{
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
if (isStringCollectionElementType)
|
||||
{
|
||||
ScanStringCollection(value, context);
|
||||
|
|
@ -81,7 +79,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
var item = list[i];
|
||||
if (item != null)
|
||||
ScanItem(item, elementWrapper, context, nextDepth);
|
||||
ScanItem(item, elementWrapper, context);
|
||||
}
|
||||
}
|
||||
else if (value is IEnumerable enumerable)
|
||||
|
|
@ -89,7 +87,7 @@ public static partial class AcBinarySerializer
|
|||
foreach (var item in enumerable)
|
||||
{
|
||||
if (item != null)
|
||||
ScanItem(item, elementWrapper, context, nextDepth);
|
||||
ScanItem(item, elementWrapper, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +102,7 @@ public static partial class AcBinarySerializer
|
|||
var genWriter = wrapper.GeneratedWriter;
|
||||
if (genWriter != null && context.Options.UseGeneratedCode)
|
||||
{
|
||||
genWriter.ScanObject(value, context, depth);
|
||||
genWriter.ScanObject(value, context);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +156,17 @@ public static partial class AcBinarySerializer
|
|||
}
|
||||
|
||||
// Fallback: runtime property loop (no SGen writer for this type)
|
||||
// Global recursion depth safety net — only when ReferenceHandling != All
|
||||
// (HasAllRefHandling=true tracks every type → 2nd-occurrence early-return above already prevents cycles).
|
||||
var needsDepthCheck = !context.HasAllRefHandling;
|
||||
if (needsDepthCheck)
|
||||
{
|
||||
if (context.RecursionDepth >= context.MaxDepth) throw new InvalidOperationException($"AcBinary scan: recursion depth exceeded MaxDepth={context.MaxDepth}");
|
||||
context.RecursionDepth++;
|
||||
}
|
||||
|
||||
var refProperties = metadata.ReferenceProperties;
|
||||
var hasPropertyFilter = context.HasPropertyFilter;
|
||||
var nextDepth2 = depth + 1;
|
||||
|
||||
for (var i = 0; i < refProperties.Length; i++)
|
||||
{
|
||||
|
|
@ -199,10 +205,12 @@ public static partial class AcBinarySerializer
|
|||
propWrapper = context.GetWrapper(runtimeType);
|
||||
wrapper.SetPropertyTypeWrapper(prop.ComplexPropertyIndex, propWrapper);
|
||||
}
|
||||
ScanValue(propValue, propWrapper, context, nextDepth2);
|
||||
ScanValue(propValue, propWrapper, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsDepthCheck) context.RecursionDepth--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -218,12 +226,13 @@ public static partial class AcBinarySerializer
|
|||
internal static void ScanValueGenerated<TOutput>(
|
||||
object value,
|
||||
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(TypeMetadataBase.RequiredMembers)] Type type,
|
||||
BinarySerializationContext<TOutput> context,
|
||||
int depth)
|
||||
BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
// Bridge: ScanValue still has int depth in its signature (other internal callers use it).
|
||||
// Pass 0 placeholder; full depth-cleanup of ScanValue chain is pending.
|
||||
var wrapper = context.GetWrapper(type);
|
||||
ScanValue(value, wrapper, context, depth);
|
||||
ScanValue(value, wrapper, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -255,12 +264,12 @@ public static partial class AcBinarySerializer
|
|||
/// falls back to GetWrapper for polymorphic items.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ScanItem<TOutput>(object item, TypeMetadataWrapper<BinarySerializeTypeMetadata>? elementWrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void ScanItem<TOutput>(object item, TypeMetadataWrapper<BinarySerializeTypeMetadata>? elementWrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var itemType = item.GetType();
|
||||
var itemWrapper = itemType == elementWrapper!.Metadata.SourceType ? elementWrapper : context.GetWrapper(itemType);
|
||||
|
||||
ScanValue(item, itemWrapper, context, depth);
|
||||
ScanValue(item, itemWrapper, context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public static partial class AcBinarySerializer
|
|||
|
||||
// Run serialization to trigger callbacks
|
||||
context.WriteHeader();
|
||||
WriteValue(value, runtimeType, context, 0);
|
||||
WriteValue(value, runtimeType, context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -328,7 +328,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteObject(value, wrapper, context, 0);
|
||||
WriteObject(value, wrapper, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
|
||||
|
|
@ -340,7 +340,7 @@ public static partial class AcBinarySerializer
|
|||
var actualValue = ConvertExpressionValue(value, ref runtimeType);
|
||||
ScanForDuplicates(actualValue, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(actualValue, runtimeType, context, 0);
|
||||
WriteValue(actualValue, runtimeType, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
|
||||
|
|
@ -374,7 +374,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteObject(value, wrapper, context, 0);
|
||||
WriteObject(value, wrapper, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
|
||||
|
|
@ -385,7 +385,7 @@ public static partial class AcBinarySerializer
|
|||
var actualValue = ConvertExpressionValue(value, ref runtimeType);
|
||||
ScanForDuplicates(actualValue, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(actualValue, runtimeType, context, 0);
|
||||
WriteValue(actualValue, runtimeType, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
|
||||
|
|
@ -447,7 +447,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteObject(value, wrapper, context, 0);
|
||||
WriteObject(value, wrapper, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithBufferWriter(context);
|
||||
|
|
@ -462,7 +462,7 @@ public static partial class AcBinarySerializer
|
|||
var actualValue = ConvertExpressionValue(value, ref runtimeType);
|
||||
ScanForDuplicates(actualValue, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(actualValue, runtimeType, context, 0);
|
||||
WriteValue(actualValue, runtimeType, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithBufferWriter(context);
|
||||
|
|
@ -507,7 +507,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteObject(value, wrapper, context, 0);
|
||||
WriteObject(value, wrapper, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithBufferWriter(context);
|
||||
|
|
@ -521,7 +521,7 @@ public static partial class AcBinarySerializer
|
|||
var actualValue = ConvertExpressionValue(value, ref runtimeType);
|
||||
ScanForDuplicates(actualValue, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(actualValue, runtimeType, context, 0);
|
||||
WriteValue(actualValue, runtimeType, context);
|
||||
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithBufferWriter(context);
|
||||
|
|
@ -721,7 +721,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteObject(value, wrapper, context, 0);
|
||||
WriteObject(value, wrapper, context);
|
||||
|
||||
if (options.UseCompression != Compression.Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithPipeWriter(context);
|
||||
|
|
@ -735,7 +735,7 @@ public static partial class AcBinarySerializer
|
|||
var actualValue = ConvertExpressionValue(value, ref runtimeType);
|
||||
ScanForDuplicates(actualValue, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(actualValue, runtimeType, context, 0);
|
||||
WriteValue(actualValue, runtimeType, context);
|
||||
|
||||
if (options.UseCompression != Compression.Lz4CompressionMode.None)
|
||||
ThrowCompressionNotSupportedWithPipeWriter(context);
|
||||
|
|
@ -766,7 +766,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(value, runtimeType, context, 0);
|
||||
WriteValue(value, runtimeType, context);
|
||||
return context.Position;
|
||||
}
|
||||
finally
|
||||
|
|
@ -791,7 +791,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
ScanForDuplicates(value, runtimeType, context);
|
||||
context.WriteHeader();
|
||||
WriteValue(value, runtimeType, context, 0);
|
||||
WriteValue(value, runtimeType, context);
|
||||
|
||||
// If compression enabled, compress directly from buffer span (1 allocation)
|
||||
if (options.UseCompression != Lz4CompressionMode.None)
|
||||
|
|
@ -891,10 +891,10 @@ public static partial class AcBinarySerializer
|
|||
/// Equivalent to the runtime's WriteValueNonPrimitive path.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void WriteValueGenerated<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context, int depth)
|
||||
internal static void WriteValueGenerated<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
WriteValueNonPrimitive(value, type, context, depth);
|
||||
WriteValueNonPrimitive(value, type, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -930,17 +930,11 @@ public static partial class AcBinarySerializer
|
|||
/// Uses pre-resolved wrapper type to avoid GetWrapper dictionary lookup.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void WriteObjectGenerated<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context, int depth)
|
||||
internal static void WriteObjectGenerated<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Null);
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(type);
|
||||
WriteObject(value, wrapper, context, depth);
|
||||
WriteObject(value, wrapper, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -949,24 +943,18 @@ public static partial class AcBinarySerializer
|
|||
/// First call per slot per context: populates slot from GetWrapper. Subsequent calls: direct array index.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void WriteObjectGenerated<TOutput>(object value, Type type, int wrapperSlot, BinarySerializationContext<TOutput> context, int depth)
|
||||
internal static void WriteObjectGenerated<TOutput>(object value, Type type, int wrapperSlot, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Null);
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(type, wrapperSlot);
|
||||
WriteObject(value, wrapper, context, depth);
|
||||
WriteObject(value, wrapper, context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Value Writing
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteValue<TOutput>(object? value, Type type, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteValue<TOutput>(object? value, Type type, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
if (value == null)
|
||||
|
|
@ -979,7 +967,7 @@ public static partial class AcBinarySerializer
|
|||
if (TryWritePrimitive(value, type, context))
|
||||
return;
|
||||
|
||||
WriteValueNonPrimitive(value, type, context, depth);
|
||||
WriteValueNonPrimitive(value, type, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -987,7 +975,7 @@ public static partial class AcBinarySerializer
|
|||
/// Skips null check and TryWritePrimitive — caller guarantees value is non-null and not a primitive type.
|
||||
/// Called from WritePropertyOrSkip default case (PropertyAccessorType.Object) and WriteValue fallback.
|
||||
/// </summary>
|
||||
private static void WriteValueNonPrimitive<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteValueNonPrimitive<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
// Nullable<T> where T is a value type: boxed value may be a primitive.
|
||||
|
|
@ -998,12 +986,6 @@ public static partial class AcBinarySerializer
|
|||
return;
|
||||
}
|
||||
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle byte arrays specially (value-like, no reference tracking)
|
||||
if (value is byte[] byteArray)
|
||||
{
|
||||
|
|
@ -1014,7 +996,7 @@ public static partial class AcBinarySerializer
|
|||
// Handle dictionaries
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
WriteDictionary(dictionary, context, depth);
|
||||
WriteDictionary(dictionary, context);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1024,19 +1006,19 @@ public static partial class AcBinarySerializer
|
|||
// Handle collections/arrays
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType))
|
||||
{
|
||||
WriteArray(enumerable, wrapper, context, depth);
|
||||
WriteArray(enumerable, wrapper, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle complex objects with single-pass reference tracking
|
||||
WriteObject(value, wrapper, context, depth);
|
||||
WriteObject(value, wrapper, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a non-primitive value with a pre-resolved wrapper (from PropertyTypeWrappers cache).
|
||||
/// Avoids GetWrapper dictionary lookup. Handles byte[], dictionary, collection, and complex objects.
|
||||
/// </summary>
|
||||
private static void WriteValueNonPrimitiveWithWrapper<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteValueNonPrimitiveWithWrapper<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var type = wrapper.Metadata.SourceType;
|
||||
|
|
@ -1048,12 +1030,6 @@ public static partial class AcBinarySerializer
|
|||
return;
|
||||
}
|
||||
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle byte arrays specially (value-like, no reference tracking)
|
||||
if (value is byte[] byteArray)
|
||||
{
|
||||
|
|
@ -1064,19 +1040,19 @@ public static partial class AcBinarySerializer
|
|||
// Handle dictionaries
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
WriteDictionary(dictionary, context, depth);
|
||||
WriteDictionary(dictionary, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle collections/arrays
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType))
|
||||
{
|
||||
WriteArray(enumerable, wrapper, context, depth);
|
||||
WriteArray(enumerable, wrapper, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle complex objects
|
||||
WriteObject(value, wrapper, context, depth);
|
||||
WriteObject(value, wrapper, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1085,7 +1061,7 @@ public static partial class AcBinarySerializer
|
|||
/// delegates to WriteObjectPolymorphic for combined poly+ref marker handling.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void WriteValueNonPrimitiveWithWrapperPoly<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth, Type polyRuntimeType)
|
||||
private static void WriteValueNonPrimitiveWithWrapperPoly<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, Type polyRuntimeType)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var type = wrapper.Metadata.SourceType;
|
||||
|
|
@ -1096,13 +1072,6 @@ public static partial class AcBinarySerializer
|
|||
return;
|
||||
}
|
||||
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.WritePolymorphicPrefix(polyRuntimeType);
|
||||
context.WriteByte(BinaryTypeCode.Null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is byte[] byteArray)
|
||||
{
|
||||
context.WritePolymorphicPrefix(polyRuntimeType);
|
||||
|
|
@ -1113,19 +1082,19 @@ public static partial class AcBinarySerializer
|
|||
if (value is IDictionary dictionary)
|
||||
{
|
||||
context.WritePolymorphicPrefix(polyRuntimeType);
|
||||
WriteDictionary(dictionary, context, depth);
|
||||
WriteDictionary(dictionary, context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType))
|
||||
{
|
||||
context.WritePolymorphicPrefix(polyRuntimeType);
|
||||
WriteArray(enumerable, wrapper, context, depth);
|
||||
WriteArray(enumerable, wrapper, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Complex object — handles combined poly+ref markers
|
||||
WriteObjectPolymorphic(value, wrapper, context, depth, polyRuntimeType);
|
||||
WriteObjectPolymorphic(value, wrapper, context, polyRuntimeType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1501,7 +1470,7 @@ public static partial class AcBinarySerializer
|
|||
#region Complex Type Writers
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteObject<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteObject<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var metadata = wrapper.Metadata;
|
||||
|
|
@ -1511,9 +1480,9 @@ public static partial class AcBinarySerializer
|
|||
if (context.UseTypeReferenceHandling(metadata))
|
||||
{
|
||||
if (useMetaForType)
|
||||
WriteObjectWithRefHandlingMeta(value, wrapper, context, depth);
|
||||
WriteObjectWithRefHandlingMeta(value, wrapper, context);
|
||||
else
|
||||
WriteObjectWithRefHandling(value, wrapper, context, depth);
|
||||
WriteObjectWithRefHandling(value, wrapper, context);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1540,7 +1509,7 @@ public static partial class AcBinarySerializer
|
|||
context.WriteByte(BinaryTypeCode.Object);
|
||||
}
|
||||
|
||||
WriteObjectProperties(value, wrapper, context, depth, useMetaForType);
|
||||
WriteObjectProperties(value, wrapper, context, useMetaForType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1548,7 +1517,7 @@ public static partial class AcBinarySerializer
|
|||
/// Cold path: only IId types with ref tracking enabled.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteObjectWithRefHandling<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteObjectWithRefHandling<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
// Reference handling: consume pre-computed write plan entry from scan pass cursor
|
||||
|
|
@ -1586,7 +1555,7 @@ public static partial class AcBinarySerializer
|
|||
context.WriteByte(BinaryTypeCode.Object);
|
||||
}
|
||||
|
||||
WriteObjectProperties(value, wrapper, context, depth, useMetaForType: false);
|
||||
WriteObjectProperties(value, wrapper, context, useMetaForType: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1594,7 +1563,7 @@ public static partial class AcBinarySerializer
|
|||
/// Cold path: IId types with ref tracking + UseMetadata enabled.
|
||||
/// </summary>
|
||||
//[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void WriteObjectWithRefHandlingMeta<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteObjectWithRefHandlingMeta<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var isFirstMetadataOccurrence = BinarySerializationContext<TOutput>.RegisterMetadataType(wrapper);
|
||||
|
|
@ -1627,39 +1596,49 @@ public static partial class AcBinarySerializer
|
|||
}
|
||||
context.WriteInlineMetadata(wrapper.Metadata, isFirstMetadataOccurrence);
|
||||
|
||||
WriteObjectProperties(value, wrapper, context, depth, useMetaForType: true);
|
||||
WriteObjectProperties(value, wrapper, context, useMetaForType: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shared property writing loop — used by WriteObject, WriteObjectWithRefHandling, WriteObjectPolymorphic.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteObjectProperties<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth, bool useMetaForType)
|
||||
private static void WriteObjectProperties<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, bool useMetaForType)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
if (context.UseGeneratedCode)
|
||||
{
|
||||
var generatedWriter = wrapper.GeneratedWriter;
|
||||
if (generatedWriter != null)
|
||||
{
|
||||
generatedWriter.WriteProperties(value, context, nextDepth);
|
||||
// SGen path handles its own RecursionDepth inc/dec via generated emit (gated on !HasAllRefHandling)
|
||||
generatedWriter.WriteProperties(value, context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime path: global recursion depth safety net — only when ReferenceHandling != All
|
||||
// (HasAllRefHandling=true tracks every type → write plan already prevents cycles via ObjectRef).
|
||||
var needsDepthCheck = !context.HasAllRefHandling;
|
||||
if (needsDepthCheck)
|
||||
{
|
||||
if (context.RecursionDepth >= context.MaxDepth) throw new InvalidOperationException($"AcBinary serialize: recursion depth exceeded MaxDepth={context.MaxDepth}");
|
||||
context.RecursionDepth++;
|
||||
}
|
||||
|
||||
if (!useMetaForType)
|
||||
{
|
||||
WritePropertiesMarkerless(value, wrapper, context, nextDepth);
|
||||
WritePropertiesMarkerless(value, wrapper, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePropertiesWithMeta(value, wrapper, context, nextDepth);
|
||||
WritePropertiesWithMeta(value, wrapper, context);
|
||||
}
|
||||
|
||||
if (needsDepthCheck) context.RecursionDepth--;
|
||||
}
|
||||
|
||||
private static void WritePropertiesWithMeta<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int nextDepth) where TOutput : struct, IBinaryOutputBase
|
||||
private static void WritePropertiesWithMeta<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var properties = wrapper.Metadata.Properties;
|
||||
var propCount = properties.Length;
|
||||
|
|
@ -1679,12 +1658,12 @@ public static partial class AcBinarySerializer
|
|||
continue;
|
||||
}
|
||||
|
||||
WritePropertyOrSkip(value, prop, wrapper, context, nextDepth);
|
||||
WritePropertyOrSkip(value, prop, wrapper, context);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WritePropertiesMarkerless<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int nextDepth) where TOutput : struct, IBinaryOutputBase
|
||||
private static void WritePropertiesMarkerless<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var properties = wrapper.Metadata.Properties;
|
||||
var propCount = properties.Length;
|
||||
|
|
@ -1701,7 +1680,7 @@ public static partial class AcBinarySerializer
|
|||
}
|
||||
else
|
||||
{
|
||||
WritePropertyOrSkip(value, prop, wrapper, context, nextDepth);
|
||||
WritePropertyOrSkip(value, prop, wrapper, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1742,7 +1721,7 @@ public static partial class AcBinarySerializer
|
|||
/// Poly always implies UseMetadata=false (checked in WritePropertyOrSkip).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void WriteObjectPolymorphic<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth, Type polyRuntimeType)
|
||||
private static void WriteObjectPolymorphic<TOutput>(object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, Type polyRuntimeType)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
var metadata = wrapper.Metadata;
|
||||
|
|
@ -1769,7 +1748,7 @@ public static partial class AcBinarySerializer
|
|||
// Poly marker (handles combined poly+ref)
|
||||
WritePolymorphicMarker(context, polyRuntimeType, cachedObjectCacheIndex);
|
||||
|
||||
WriteObjectProperties(value, wrapper, context, depth, false);
|
||||
WriteObjectProperties(value, wrapper, context, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1916,7 +1895,7 @@ public static partial class AcBinarySerializer
|
|||
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2072",
|
||||
Justification = "Polymorphism via obj.GetType() is a documented trimmer blind spot. Consumers must root "
|
||||
+ "polymorphic concrete types via [AcBinarySerializable] (SGen) or TrimmerRootAssembly.")]
|
||||
private static void WritePropertyOrSkip<TOutput>(object obj, BinaryPropertyAccessor prop, TypeMetadataWrapper<BinarySerializeTypeMetadata> parentWrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WritePropertyOrSkip<TOutput>(object obj, BinaryPropertyAccessor prop, TypeMetadataWrapper<BinarySerializeTypeMetadata> parentWrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
switch (prop.AccessorType)
|
||||
|
|
@ -2013,16 +1992,16 @@ public static partial class AcBinarySerializer
|
|||
parentWrapper.SetPropertyTypeWrapper(complexIdx, propWrapper);
|
||||
}
|
||||
if (!context.UseMetadata && prop.IsPolymorphicCandidate && runtimeType != prop.PropertyType)
|
||||
WriteValueNonPrimitiveWithWrapperPoly(value, propWrapper, context, depth, runtimeType);
|
||||
WriteValueNonPrimitiveWithWrapperPoly(value, propWrapper, context, runtimeType);
|
||||
else
|
||||
WriteValueNonPrimitiveWithWrapper(value, propWrapper, context, depth);
|
||||
WriteValueNonPrimitiveWithWrapper(value, propWrapper, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-complex in default case (nullable value type, etc.)
|
||||
if (!context.UseMetadata && prop.IsPolymorphicCandidate && runtimeType != prop.PropertyType)
|
||||
context.WritePolymorphicPrefix(runtimeType);
|
||||
WriteValueNonPrimitive(value, runtimeType, context, depth);
|
||||
WriteValueNonPrimitive(value, runtimeType, context);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -2037,11 +2016,10 @@ public static partial class AcBinarySerializer
|
|||
/// <summary>
|
||||
/// Optimized array writer with specialized paths for primitive collections.
|
||||
/// </summary>
|
||||
private static void WriteArray<TOutput>(IEnumerable enumerable, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteArray<TOutput>(IEnumerable enumerable, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Array);
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
// Use pre-computed metadata — no GetWrapper or GetCollectionElementType needed
|
||||
var metadata = wrapper.Metadata;
|
||||
|
|
@ -2063,7 +2041,7 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
var item = list[i];
|
||||
var itemType = item?.GetType() ?? typeof(object);
|
||||
WriteValue(item, itemType, context, nextDepth);
|
||||
WriteValue(item, itemType, context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -2075,7 +2053,7 @@ public static partial class AcBinarySerializer
|
|||
foreach (var item in enumerable)
|
||||
{
|
||||
var itemType = item?.GetType() ?? typeof(object);
|
||||
WriteValue(item, itemType, context, nextDepth);
|
||||
WriteValue(item, itemType, context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -2091,7 +2069,7 @@ public static partial class AcBinarySerializer
|
|||
foreach (var item in items)
|
||||
{
|
||||
var itemType = item?.GetType() ?? typeof(object);
|
||||
WriteValue(item, itemType, context, nextDepth);
|
||||
WriteValue(item, itemType, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2376,22 +2354,21 @@ public static partial class AcBinarySerializer
|
|||
return false;
|
||||
}
|
||||
|
||||
private static void WriteDictionary<TOutput>(IDictionary dictionary, BinarySerializationContext<TOutput> context, int depth)
|
||||
private static void WriteDictionary<TOutput>(IDictionary dictionary, BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase
|
||||
{
|
||||
context.WriteByte(BinaryTypeCode.Dictionary);
|
||||
context.WriteVarUInt((uint)dictionary.Count);
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
foreach (DictionaryEntry entry in dictionary)
|
||||
{
|
||||
// Write key
|
||||
var keyType = entry.Key?.GetType() ?? typeof(object);
|
||||
WriteValue(entry.Key, keyType, context, nextDepth);
|
||||
WriteValue(entry.Key, keyType, context);
|
||||
|
||||
// Write value
|
||||
var valueType = entry.Value?.GetType() ?? typeof(object);
|
||||
WriteValue(entry.Value, valueType, context, nextDepth);
|
||||
WriteValue(entry.Value, valueType, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,10 @@ internal interface IGeneratedBinaryReader
|
|||
/// UseMetadata=true falls back to runtime path (cross-type CacheMap not known at compile time).
|
||||
/// </summary>
|
||||
/// <param name="context">The deserialization context (owns buffer, position, options).</param>
|
||||
/// <param name="depth">Current depth in the object graph.</param>
|
||||
/// <param name="cacheIndex">-1 = not cached, 0+ = register at this cache index for ref tracking.</param>
|
||||
/// <typeparam name="TInput">Input strategy (ArrayBinaryInput or SequenceBinaryInput).</typeparam>
|
||||
/// <returns>The deserialized object, or null if creation failed.</returns>
|
||||
object? ReadObject<TInput>(AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, int depth, int cacheIndex)
|
||||
object? ReadObject<TInput>(AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, int cacheIndex)
|
||||
where TInput : struct, IBinaryInputBase;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,8 +33,7 @@ internal interface IGeneratedBinaryReader
|
|||
/// </summary>
|
||||
/// <param name="value">The pre-created instance to populate. Implementation casts to the concrete type.</param>
|
||||
/// <param name="context">The deserialization context (owns buffer, position, options).</param>
|
||||
/// <param name="depth">Current depth in the object graph.</param>
|
||||
/// <typeparam name="TInput">Input strategy (ArrayBinaryInput or SequenceBinaryInput).</typeparam>
|
||||
void ReadProperties<TInput>(object value, AcBinaryDeserializer.BinaryDeserializationContext<TInput> context, int depth)
|
||||
void ReadProperties<TInput>(object value, AcBinaryDeserializer.BinaryDeserializationContext<TInput> context)
|
||||
where TInput : struct, IBinaryInputBase;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ internal interface IGeneratedBinaryWriter
|
|||
/// </summary>
|
||||
/// <param name="value">The object whose properties to write. Implementation casts to the concrete type.</param>
|
||||
/// <param name="context">The serialization context (owns buffer, position, options).</param>
|
||||
/// <param name="depth">Current depth in the object graph (for nested object serialization).</param>
|
||||
/// <typeparam name="TOutput">Output strategy (ArrayBinaryOutput or BufferWriterBinaryOutput).</typeparam>
|
||||
void WriteProperties<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth)
|
||||
void WriteProperties<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -33,7 +32,7 @@ internal interface IGeneratedBinaryWriter
|
|||
/// Replaces the entire runtime ScanValue for SGen types — no GetWrapper, no delegate invoke.
|
||||
/// Called from ScanForDuplicates or from parent SGen ScanObject (child).
|
||||
/// </summary>
|
||||
void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth)
|
||||
void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context)
|
||||
where TOutput : struct, IBinaryOutputBase;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@ public static partial class AcJsonDeserializer
|
|||
#region With Reference Handling (JsonElement Path)
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadValue(in JsonElement element, in Type targetType, DeserializationContext context, int depth)
|
||||
private static object? ReadValue(in JsonElement element, in Type targetType, DeserializationContext context)
|
||||
{
|
||||
var kind = element.ValueKind;
|
||||
if (kind == JsonValueKind.Object) return ReadObject(element, targetType, context, depth);
|
||||
if (kind == JsonValueKind.Array) return ReadArray(element, targetType, context, depth);
|
||||
if (kind == JsonValueKind.Object) return ReadObject(element, targetType, context);
|
||||
if (kind == JsonValueKind.Array) return ReadArray(element, targetType, context);
|
||||
if (kind == JsonValueKind.Null || kind == JsonValueKind.Undefined) return null;
|
||||
return ReadPrimitive(element, targetType, kind);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObject(in JsonElement element, in Type targetType, DeserializationContext context, int depth)
|
||||
private static object? ReadObject(in JsonElement element, in Type targetType, DeserializationContext context)
|
||||
{
|
||||
// Check for $ref first - support both string (Newtonsoft) and int formats
|
||||
if (element.TryGetProperty(RefPropertyUtf8, out var refElement))
|
||||
|
|
@ -31,10 +31,8 @@ public static partial class AcJsonDeserializer
|
|||
return context.TryGetReferencedObject(refId, out var refObj) ? refObj : new DeferredReference(refId, targetType);
|
||||
}
|
||||
|
||||
if (depth > context.MaxDepth) return null;
|
||||
|
||||
if (IsDictionaryType(targetType, out var keyType, out var valueType))
|
||||
return ReadDictionary(element, keyType!, valueType!, context, depth);
|
||||
return ReadDictionary(element, keyType!, valueType!, context);
|
||||
|
||||
var metadata = GetTypeMetadata(targetType);
|
||||
|
||||
|
|
@ -56,7 +54,7 @@ public static partial class AcJsonDeserializer
|
|||
if (element.TryGetProperty(IdPropertyUtf8, out var idElement))
|
||||
context.RegisterObject(ParseRefId(idElement), instance);
|
||||
|
||||
PopulateObjectInternal(element, instance, metadata, context, depth);
|
||||
PopulateObjectInternal(element, instance, metadata, context);
|
||||
|
||||
// ChainMode: Use cached IId info from metadata (no runtime reflection!)
|
||||
if (context.IsChainMode && metadata.IsIId && metadata.IdGetter != null && metadata.IdType != null)
|
||||
|
|
@ -122,10 +120,9 @@ public static partial class AcJsonDeserializer
|
|||
}
|
||||
}
|
||||
|
||||
private static void PopulateObjectInternal(in JsonElement element, object target, JsonDeserializeTypeMetadata metadata, DeserializationContext context, int depth)
|
||||
private static void PopulateObjectInternal(in JsonElement element, object target, JsonDeserializeTypeMetadata metadata, DeserializationContext context)
|
||||
{
|
||||
var propsDict = metadata.PropertySettersFrozen;
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
foreach (var jsonProp in element.EnumerateObject())
|
||||
{
|
||||
|
|
@ -136,7 +133,7 @@ public static partial class AcJsonDeserializer
|
|||
|
||||
if (!propsDict.TryGetValue(propName, out var propInfo)) continue;
|
||||
|
||||
var value = ReadValue(jsonProp.Value, propInfo.PropertyType, context, nextDepth);
|
||||
var value = ReadValue(jsonProp.Value, propInfo.PropertyType, context);
|
||||
|
||||
if (value is DeferredReference deferred)
|
||||
context.AddPropertyToResolve(target, propInfo, deferred.RefId);
|
||||
|
|
@ -145,11 +142,9 @@ public static partial class AcJsonDeserializer
|
|||
}
|
||||
}
|
||||
|
||||
private static void PopulateObjectInternalMerge(in JsonElement element, object target, JsonDeserializeTypeMetadata metadata, DeserializationContext context, int depth)
|
||||
private static void PopulateObjectInternalMerge(in JsonElement element, object target, JsonDeserializeTypeMetadata metadata, DeserializationContext context)
|
||||
{
|
||||
var propsDict = metadata.PropertySettersFrozen;
|
||||
var nextDepth = depth + 1;
|
||||
var maxDepthReached = nextDepth > context.MaxDepth;
|
||||
|
||||
foreach (var jsonProp in element.EnumerateObject())
|
||||
{
|
||||
|
|
@ -163,23 +158,13 @@ public static partial class AcJsonDeserializer
|
|||
var propValue = jsonProp.Value;
|
||||
var propValueKind = propValue.ValueKind;
|
||||
|
||||
if (maxDepthReached)
|
||||
{
|
||||
if (propValueKind != JsonValueKind.Object && propValueKind != JsonValueKind.Array)
|
||||
{
|
||||
var primitiveValue = ReadPrimitive(propValue, propInfo.PropertyType, propValueKind);
|
||||
propInfo.SetValue(target, primitiveValue);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle IId collection merge
|
||||
if (propInfo.IsIIdCollection && propValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var existingCollection = propInfo.GetValue(target);
|
||||
if (existingCollection != null)
|
||||
{
|
||||
MergeIIdCollection(propValue, existingCollection, propInfo, context, depth);
|
||||
MergeIIdCollection(propValue, existingCollection, propInfo, context);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +175,7 @@ public static partial class AcJsonDeserializer
|
|||
// Check for $ref
|
||||
if (propValue.TryGetProperty(RefPropertyUtf8, out _))
|
||||
{
|
||||
var value = ReadValue(propValue, propInfo.PropertyType, context, nextDepth);
|
||||
var value = ReadValue(propValue, propInfo.PropertyType, context);
|
||||
if (value is DeferredReference deferred)
|
||||
context.AddPropertyToResolve(target, propInfo, deferred.RefId);
|
||||
else
|
||||
|
|
@ -205,13 +190,13 @@ public static partial class AcJsonDeserializer
|
|||
if (existingObj != null)
|
||||
{
|
||||
var nestedMetadata = GetTypeMetadata(propInfo.PropertyType);
|
||||
PopulateObjectInternalMerge(propValue, existingObj, nestedMetadata, context, nextDepth);
|
||||
PopulateObjectInternalMerge(propValue, existingObj, nestedMetadata, context);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var value2 = ReadValue(propValue, propInfo.PropertyType, context, nextDepth);
|
||||
var value2 = ReadValue(propValue, propInfo.PropertyType, context);
|
||||
|
||||
if (value2 is DeferredReference deferred2)
|
||||
context.AddPropertyToResolve(target, propInfo, deferred2.RefId);
|
||||
|
|
@ -220,10 +205,8 @@ public static partial class AcJsonDeserializer
|
|||
}
|
||||
}
|
||||
|
||||
private static void PopulateList(in JsonElement arrayElement, IList targetList, in Type listType, DeserializationContext context, int depth)
|
||||
private static void PopulateList(in JsonElement arrayElement, IList targetList, in Type listType, DeserializationContext context)
|
||||
{
|
||||
if (depth > context.MaxDepth) return;
|
||||
|
||||
var elementType = GetCollectionElementType(listType);
|
||||
if (elementType == null) return;
|
||||
|
||||
|
|
@ -233,7 +216,6 @@ public static partial class AcJsonDeserializer
|
|||
try
|
||||
{
|
||||
targetList.Clear();
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
// ChainMode: Use cached IId info from element type metadata (no runtime reflection!)
|
||||
JsonDeserializeTypeMetadata? elementMetadata = null;
|
||||
|
|
@ -244,7 +226,7 @@ public static partial class AcJsonDeserializer
|
|||
|
||||
foreach (var item in arrayElement.EnumerateArray())
|
||||
{
|
||||
var value = ReadValue(item, elementType, context, nextDepth);
|
||||
var value = ReadValue(item, elementType, context);
|
||||
|
||||
// ChainMode: Check if we already have this IId object using cached metadata
|
||||
if (context.IsChainMode && value != null && elementMetadata != null &&
|
||||
|
|
@ -270,20 +252,16 @@ public static partial class AcJsonDeserializer
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadArray(in JsonElement element, in Type targetType, DeserializationContext context, int depth)
|
||||
private static object? ReadArray(in JsonElement element, in Type targetType, DeserializationContext context)
|
||||
{
|
||||
if (depth > context.MaxDepth) return null;
|
||||
|
||||
var elementType = GetCollectionElementType(targetType);
|
||||
if (elementType == null) return null;
|
||||
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
var list = GetOrCreateListFactory(elementType)(0);
|
||||
foreach (var item in element.EnumerateArray())
|
||||
list.Add(ReadValue(item, elementType, context, nextDepth));
|
||||
list.Add(ReadValue(item, elementType, context));
|
||||
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
list.CopyTo(array, 0);
|
||||
|
|
@ -306,14 +284,14 @@ public static partial class AcJsonDeserializer
|
|||
try
|
||||
{
|
||||
foreach (var item in element.EnumerateArray())
|
||||
targetList.Add(ReadValue(item, elementType, context, nextDepth));
|
||||
targetList.Add(ReadValue(item, elementType, context));
|
||||
}
|
||||
finally { acObservable?.EndUpdate(); }
|
||||
|
||||
return targetList;
|
||||
}
|
||||
|
||||
private static void MergeIIdCollection(in JsonElement arrayElement, object existingCollection, PropertySetterInfo propInfo, DeserializationContext context, int depth)
|
||||
private static void MergeIIdCollection(in JsonElement arrayElement, object existingCollection, PropertySetterInfo propInfo, DeserializationContext context)
|
||||
{
|
||||
var elementType = propInfo.ElementType!;
|
||||
var idGetter = propInfo.ElementIdGetter!;
|
||||
|
|
@ -343,7 +321,6 @@ public static partial class AcJsonDeserializer
|
|||
}
|
||||
}
|
||||
|
||||
var nextDepth = depth + 1;
|
||||
foreach (var jsonItem in arrayElement.EnumerateArray())
|
||||
{
|
||||
if (jsonItem.ValueKind != JsonValueKind.Object) continue;
|
||||
|
|
@ -357,23 +334,22 @@ public static partial class AcJsonDeserializer
|
|||
if (existingById.TryGetValue(itemId, out var existingItem))
|
||||
{
|
||||
var itemMetadata = GetTypeMetadata(elementType);
|
||||
PopulateObjectInternalMerge(jsonItem, existingItem, itemMetadata, context, nextDepth);
|
||||
PopulateObjectInternalMerge(jsonItem, existingItem, itemMetadata, context);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var newItem = ReadValue(jsonItem, elementType, context, nextDepth);
|
||||
var newItem = ReadValue(jsonItem, elementType, context);
|
||||
if (newItem != null) existingList.Add(newItem);
|
||||
}
|
||||
}
|
||||
finally { acObservable?.EndUpdate(); }
|
||||
}
|
||||
|
||||
private static object ReadDictionary(in JsonElement element, in Type keyType, in Type valueType, DeserializationContext context, int depth)
|
||||
private static object ReadDictionary(in JsonElement element, in Type keyType, in Type valueType, DeserializationContext context)
|
||||
{
|
||||
var dictType = DictionaryGenericType.MakeGenericType(keyType, valueType);
|
||||
var dict = (IDictionary)Activator.CreateInstance(dictType)!;
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
foreach (var prop in element.EnumerateObject())
|
||||
{
|
||||
|
|
@ -388,7 +364,7 @@ public static partial class AcJsonDeserializer
|
|||
else if (keyType.IsEnum) key = Enum.Parse(keyType, name);
|
||||
else key = Convert.ChangeType(name, keyType, CultureInfo.InvariantCulture);
|
||||
|
||||
dict.Add(key, ReadValue(prop.Value, valueType, context, nextDepth));
|
||||
dict.Add(key, ReadValue(prop.Value, valueType, context));
|
||||
}
|
||||
|
||||
return dict;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public static partial class AcJsonDeserializer
|
|||
var context = JsonDeserializationContextPool.Get(options);
|
||||
try
|
||||
{
|
||||
var result = ReadValue(doc.RootElement, targetType, context, 0);
|
||||
var result = ReadValue(doc.RootElement, targetType, context);
|
||||
context.ResolveReferences();
|
||||
return (T?)result;
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ public static partial class AcJsonDeserializer
|
|||
var context = JsonDeserializationContextPool.Get(options);
|
||||
try
|
||||
{
|
||||
var result = ReadValue(doc.RootElement, targetType, context, 0);
|
||||
var result = ReadValue(doc.RootElement, targetType, context);
|
||||
context.ResolveReferences();
|
||||
return (T?)result;
|
||||
}
|
||||
|
|
@ -197,7 +197,7 @@ public static partial class AcJsonDeserializer
|
|||
var context = JsonDeserializationContextPool.Get(options);
|
||||
try
|
||||
{
|
||||
var result = ReadValue(doc.RootElement, targetType, context, 0);
|
||||
var result = ReadValue(doc.RootElement, targetType, context);
|
||||
context.ResolveReferences();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -282,7 +282,7 @@ public static partial class AcJsonDeserializer
|
|||
var context = JsonDeserializationContextPool.Get(options);
|
||||
try
|
||||
{
|
||||
var result = ReadValue(doc.RootElement, targetType, context, 0);
|
||||
var result = ReadValue(doc.RootElement, targetType, context);
|
||||
context.ResolveReferences();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -398,7 +398,7 @@ public static partial class AcJsonDeserializer
|
|||
if (rootElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
if (target is IList targetList)
|
||||
PopulateList(rootElement, targetList, targetType, context, 0);
|
||||
PopulateList(rootElement, targetList, targetType, context);
|
||||
else
|
||||
throw new AcJsonDeserializationException($"Cannot populate non-list target '{targetType.Name}' with JSON array", json, targetType);
|
||||
|
||||
|
|
@ -409,7 +409,7 @@ public static partial class AcJsonDeserializer
|
|||
if (rootElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var metadata = GetTypeMetadata(targetType);
|
||||
PopulateObjectInternalMerge(rootElement, target, metadata, context, 0);
|
||||
PopulateObjectInternalMerge(rootElement, target, metadata, context);
|
||||
}
|
||||
else
|
||||
throw new AcJsonDeserializationException($"Cannot populate object with JSON value of kind '{rootElement.ValueKind}'", json, targetType);
|
||||
|
|
@ -502,7 +502,7 @@ public static partial class AcJsonDeserializer
|
|||
|
||||
try
|
||||
{
|
||||
var result = ReadValue(doc.RootElement, targetType, context, 0);
|
||||
var result = ReadValue(doc.RootElement, targetType, context);
|
||||
context.ResolveReferences();
|
||||
return new JsonDeserializeChain<T>(doc, context, chainTracker, (T?)result);
|
||||
}
|
||||
|
|
@ -523,14 +523,14 @@ public static partial class AcJsonDeserializer
|
|||
if (rootElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
if (target is IList targetList)
|
||||
PopulateList(rootElement, targetList, targetType, context, 0);
|
||||
PopulateList(rootElement, targetList, targetType, context);
|
||||
else
|
||||
throw new AcJsonDeserializationException($"Cannot populate non-list target '{targetType.Name}' with JSON array", null, targetType);
|
||||
}
|
||||
else if (rootElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var metadata = GetTypeMetadata(targetType);
|
||||
PopulateObjectInternalMerge(rootElement, target, metadata, context, 0);
|
||||
PopulateObjectInternalMerge(rootElement, target, metadata, context);
|
||||
}
|
||||
else
|
||||
throw new AcJsonDeserializationException($"Cannot populate object with JSON value of kind '{rootElement.ValueKind}'", null, targetType);
|
||||
|
|
@ -571,7 +571,7 @@ public static partial class AcJsonDeserializer
|
|||
|
||||
try
|
||||
{
|
||||
var result = ReadValue(_document.RootElement, targetType, _context, 0);
|
||||
var result = ReadValue(_document.RootElement, targetType, _context);
|
||||
_context.ResolveReferences();
|
||||
return (TResult?)result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public static partial class AcJsonSerializer
|
|||
|
||||
private static void ScanReferences(object? value, JsonSerializationContext context, int depth)
|
||||
{
|
||||
if (value == null || depth > context.MaxDepth) return;
|
||||
if (value == null) return;
|
||||
|
||||
var type = value.GetType();
|
||||
if (IsPrimitiveOrStringFast(type)) return;
|
||||
|
|
@ -167,8 +167,6 @@ public static partial class AcJsonSerializer
|
|||
|
||||
if (TryWritePrimitive(value, type, context.Writer)) return;
|
||||
|
||||
if (depth > context.MaxDepth) { context.Writer.WriteNullValue(); return; }
|
||||
|
||||
if (value is IDictionary dictionary) { WriteDictionary(dictionary, context, depth); return; }
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType)) { WriteArray(enumerable, context, depth); return; }
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public static partial class AcToonSerializer
|
|||
context.WriteLine("@data {");
|
||||
context.CurrentIndentLevel++;
|
||||
|
||||
WriteValue(value, type, context, 0);
|
||||
WriteValue(value, type, context);
|
||||
context.WriteLine();
|
||||
|
||||
context.CurrentIndentLevel--;
|
||||
|
|
@ -35,7 +35,7 @@ public static partial class AcToonSerializer
|
|||
/// <summary>
|
||||
/// Write a value (dispatcher for different types).
|
||||
/// </summary>
|
||||
private static void WriteValue(object? value, Type type, ToonSerializationContext context, int depth)
|
||||
private static void WriteValue(object? value, Type type, ToonSerializationContext context)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
|
|
@ -47,12 +47,6 @@ public static partial class AcToonSerializer
|
|||
if (TryWritePrimitive(value, type, context))
|
||||
return;
|
||||
|
||||
if (depth > context.MaxDepth)
|
||||
{
|
||||
context.Write("null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for reference
|
||||
if (context.ReferenceHandling != ReferenceHandlingMode.None && context.TryGetExistingRef(value, out var refId))
|
||||
{
|
||||
|
|
@ -63,19 +57,19 @@ public static partial class AcToonSerializer
|
|||
// Handle dictionaries
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
WriteDictionary(dictionary, context, depth);
|
||||
WriteDictionary(dictionary, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle collections/arrays
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType))
|
||||
{
|
||||
WriteArray(enumerable, type, context, depth);
|
||||
WriteArray(enumerable, type, context);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle complex objects
|
||||
WriteObject(value, type, context, depth);
|
||||
WriteObject(value, type, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -223,7 +217,7 @@ public static partial class AcToonSerializer
|
|||
/// <summary>
|
||||
/// Write complex object.
|
||||
/// </summary>
|
||||
private static void WriteObject(object value, Type type, ToonSerializationContext context, int depth)
|
||||
private static void WriteObject(object value, Type type, ToonSerializationContext context)
|
||||
{
|
||||
var metadata = GetTypeMetadata(type);
|
||||
|
||||
|
|
@ -243,8 +237,6 @@ public static partial class AcToonSerializer
|
|||
context.WriteLine(" {");
|
||||
context.CurrentIndentLevel++;
|
||||
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
// Write properties
|
||||
foreach (var prop in metadata.Properties)
|
||||
{
|
||||
|
|
@ -273,7 +265,7 @@ public static partial class AcToonSerializer
|
|||
}
|
||||
else
|
||||
{
|
||||
WriteValue(propValue, prop.PropertyType, context, nextDepth);
|
||||
WriteValue(propValue, prop.PropertyType, context);
|
||||
context.WriteLine();
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +278,7 @@ public static partial class AcToonSerializer
|
|||
/// <summary>
|
||||
/// Write array/collection.
|
||||
/// </summary>
|
||||
private static void WriteArray(IEnumerable enumerable, Type type, ToonSerializationContext context, int depth)
|
||||
private static void WriteArray(IEnumerable enumerable, Type type, ToonSerializationContext context)
|
||||
{
|
||||
var elementType = GetCollectionElementType(type) ?? typeof(object);
|
||||
|
||||
|
|
@ -312,12 +304,10 @@ public static partial class AcToonSerializer
|
|||
context.WriteLine("[");
|
||||
context.CurrentIndentLevel++;
|
||||
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
context.WriteIndent();
|
||||
WriteValue(item, item?.GetType() ?? elementType, context, nextDepth);
|
||||
WriteValue(item, item?.GetType() ?? elementType, context);
|
||||
context.WriteLine();
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +367,7 @@ public static partial class AcToonSerializer
|
|||
/// <summary>
|
||||
/// Write dictionary.
|
||||
/// </summary>
|
||||
private static void WriteDictionary(IDictionary dictionary, ToonSerializationContext context, int depth)
|
||||
private static void WriteDictionary(IDictionary dictionary, ToonSerializationContext context)
|
||||
{
|
||||
// Write dictionary header with count and type information
|
||||
if (context.Options.ShowCollectionCount)
|
||||
|
|
@ -390,21 +380,19 @@ public static partial class AcToonSerializer
|
|||
context.WriteLine("{");
|
||||
context.CurrentIndentLevel++;
|
||||
|
||||
var nextDepth = depth + 1;
|
||||
|
||||
foreach (DictionaryEntry entry in dictionary)
|
||||
{
|
||||
context.WriteIndent();
|
||||
|
||||
// Write key
|
||||
var keyType = entry.Key?.GetType() ?? typeof(object);
|
||||
WriteValue(entry.Key, keyType, context, nextDepth);
|
||||
WriteValue(entry.Key, keyType, context);
|
||||
|
||||
context.Write(" => ");
|
||||
|
||||
// Write value
|
||||
var valueType = entry.Value?.GetType() ?? typeof(object);
|
||||
WriteValue(entry.Value, valueType, context, nextDepth);
|
||||
WriteValue(entry.Value, valueType, context);
|
||||
|
||||
context.WriteLine();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ public static partial class AcToonSerializer
|
|||
|
||||
private static void ScanReferences(object? value, ToonSerializationContext context, int depth)
|
||||
{
|
||||
if (value == null || depth > context.MaxDepth) return;
|
||||
if (value == null) return;
|
||||
|
||||
var type = value.GetType();
|
||||
if (IsPrimitiveOrStringFast(type)) return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue