Add IId-aware reference tracking to serializer/deserializer
Enhances both binary deserialization and JSON serialization to support IId-based reference tracking and handling. Refactors object reference reading in AcBinaryDeserializer to use a unified TryGetValue approach. Adds IId-aware tracking, ID writing, and reference lookup methods to AcJsonSerializer and updates ScanReferences and WriteObject to use them. Improves support for custom identity types and robust reference management.
This commit is contained in:
parent
6dbe4d76c1
commit
7d133a4b24
|
|
@ -913,18 +913,20 @@ public static partial class AcBinaryDeserializer
|
|||
private static object? ReadObjectRef(ref BinaryDeserializationContext context, Type targetType, int depth)
|
||||
{
|
||||
var wrapper = context.ContextClass.GetWrapper(targetType);
|
||||
return ReadObjectRef(ref context, ref wrapper, depth);
|
||||
}
|
||||
private static object? ReadObjectRef(ref BinaryDeserializationContext context, ref TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper, int depth)
|
||||
{
|
||||
var metadata = wrapper.Metadata;
|
||||
|
||||
if (metadata.IdAccessorType != AcSerializerCommon.IdAccessorType.None)
|
||||
{
|
||||
var map = wrapper.IdentityMap;
|
||||
|
||||
// Read ID based on type and lookup in IdentityMap
|
||||
return metadata.IdAccessorType switch
|
||||
{
|
||||
AcSerializerCommon.IdAccessorType.Int32 => ReadObjectRefInt32(ref context, map),
|
||||
AcSerializerCommon.IdAccessorType.Int64 => ReadObjectRefInt64(ref context, map),
|
||||
AcSerializerCommon.IdAccessorType.Guid => ReadObjectRefGuid(ref context, map),
|
||||
AcSerializerCommon.IdAccessorType.Int32 => ReadObjectRefInt32(ref context, ref wrapper),
|
||||
AcSerializerCommon.IdAccessorType.Int64 => ReadObjectRefInt64(ref context, ref wrapper),
|
||||
AcSerializerCommon.IdAccessorType.Guid => ReadObjectRefGuid(ref context, ref wrapper),
|
||||
_ => throw new Exception("metadata.IdAccessorType not valid")
|
||||
};
|
||||
}
|
||||
|
|
@ -933,30 +935,24 @@ public static partial class AcBinaryDeserializer
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObjectRefInt32(ref BinaryDeserializationContext context, AcSerializerCommon.IIdentityMap? map)
|
||||
private static object? ReadObjectRefInt32(ref BinaryDeserializationContext context, ref TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper)
|
||||
{
|
||||
var id = context.ReadVarInt();
|
||||
if (map is AcSerializerCommon.IdentityMap<int> intMap && intMap.TryGetValue(id, out var obj))
|
||||
return obj;
|
||||
return null;
|
||||
return context.ContextClass.TryGetValue(wrapper, id, out var instance) ? instance : throw new Exception("ReadObjectRefInt32");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObjectRefInt64(ref BinaryDeserializationContext context, AcSerializerCommon.IIdentityMap? map)
|
||||
private static object? ReadObjectRefInt64(ref BinaryDeserializationContext context, ref TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper)
|
||||
{
|
||||
var id = context.ReadVarLong(); // VarLong, not VarInt!
|
||||
if (map is AcSerializerCommon.IdentityMap<long> longMap && longMap.TryGetValue(id, out var obj))
|
||||
return obj;
|
||||
return null; // Long IIds don't use flat reference tracking
|
||||
var id = context.ReadVarLong();
|
||||
return context.ContextClass.TryGetValue(wrapper, id, out var instance) ? instance : throw new Exception("ReadObjectRefInt64");;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static object? ReadObjectRefGuid(ref BinaryDeserializationContext context, AcSerializerCommon.IIdentityMap? map)
|
||||
private static object? ReadObjectRefGuid(ref BinaryDeserializationContext context, ref TypeMetadataWrapper<BinaryDeserializeTypeMetadata> wrapper)
|
||||
{
|
||||
var id = context.ReadGuidUnsafe(); // 16 bytes raw!
|
||||
if (map is AcSerializerCommon.IdentityMap<Guid> guidMap && guidMap.TryGetValue(id, out var obj))
|
||||
return obj;
|
||||
return null; // Guid IIds don't use flat reference tracking
|
||||
return context.ContextClass.TryGetValue(wrapper, id, out var instance) ? instance : throw new Exception("ReadObjectRefGuid");;
|
||||
}
|
||||
|
||||
private static object? ReadObject(ref BinaryDeserializationContext context, Type targetType, int depth)
|
||||
|
|
|
|||
|
|
@ -86,15 +86,47 @@ public static partial class AcJsonSerializer
|
|||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TrackForScanning(object obj) => _refTracker.TrackForScanning(obj);
|
||||
|
||||
/// <summary>
|
||||
/// IId-aware tracking for reference scanning.
|
||||
/// Uses IId.Id for IId types, RuntimeHelpers.GetHashCode for others.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TrackForScanningWithIId(object obj, JsonSerializeTypeMetadata metadata)
|
||||
{
|
||||
int existingRefId;
|
||||
return _refTracker.TrackForScanningWithIId(obj, metadata, out existingRefId);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool ShouldWriteId(object obj, out int id) => _refTracker.ShouldWriteId(obj, out id);
|
||||
|
||||
/// <summary>
|
||||
/// IId-aware check for reference ID writing.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool ShouldWriteIdForIId(object obj, JsonSerializeTypeMetadata metadata, out int id)
|
||||
=> _refTracker.ShouldWriteIdForIId(obj, metadata, out id);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void MarkAsWritten(object obj, int id) => _refTracker.MarkAsWritten(obj, id);
|
||||
|
||||
/// <summary>
|
||||
/// IId-aware mark as written.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void MarkAsWrittenForIId(object obj, JsonSerializeTypeMetadata metadata, int id)
|
||||
=> _refTracker.MarkAsWrittenForIId(obj, metadata, id);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetExistingRef(object obj, out int refId) => _refTracker.TryGetExistingRef(obj, out refId);
|
||||
|
||||
/// <summary>
|
||||
/// IId-aware existing ref lookup.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetExistingRefForIId(object obj, JsonSerializeTypeMetadata metadata, out int refId)
|
||||
=> _refTracker.TryGetExistingRefForIId(obj, metadata, out refId);
|
||||
|
||||
public string GetResult()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -130,7 +130,13 @@ public static partial class AcJsonSerializer
|
|||
|
||||
var type = value.GetType();
|
||||
if (IsPrimitiveOrStringFast(type)) return;
|
||||
if (!context.TrackForScanning(value)) return;
|
||||
|
||||
// Get wrapper early to use IId-aware tracking
|
||||
var wrapper = context.GetWrapper(type);
|
||||
var metadata = wrapper.Metadata;
|
||||
|
||||
// Use IId-aware scanning if available
|
||||
if (!context.TrackForScanningWithIId(value, metadata)) return;
|
||||
|
||||
if (value is IEnumerable enumerable && !ReferenceEquals(type, StringType))
|
||||
{
|
||||
|
|
@ -139,8 +145,6 @@ public static partial class AcJsonSerializer
|
|||
return;
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(type);
|
||||
var metadata = wrapper.Metadata;
|
||||
var props = metadata.Properties;
|
||||
var propCount = props.Length;
|
||||
for (var i = 0; i < propCount; i++)
|
||||
|
|
@ -174,8 +178,11 @@ public static partial class AcJsonSerializer
|
|||
private static void WriteObject(object value, in Type type, JsonSerializationContext context, int depth)
|
||||
{
|
||||
var writer = context.Writer;
|
||||
var wrapper = context.GetWrapper(type);
|
||||
var metadata = wrapper.Metadata;
|
||||
|
||||
if (context.UseReferenceHandling && context.TryGetExistingRef(value, out var refId))
|
||||
// Use IId-aware reference handling
|
||||
if (context.UseReferenceHandling && context.TryGetExistingRefForIId(value, metadata, out var refId))
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString(RefPropertyEncoded, refId.ToString(CultureInfo.InvariantCulture));
|
||||
|
|
@ -185,14 +192,12 @@ public static partial class AcJsonSerializer
|
|||
|
||||
writer.WriteStartObject();
|
||||
|
||||
if (context.UseReferenceHandling && context.ShouldWriteId(value, out var id))
|
||||
if (context.UseReferenceHandling && context.ShouldWriteIdForIId(value, metadata, out var id))
|
||||
{
|
||||
writer.WriteString(IdPropertyEncoded, id.ToString(CultureInfo.InvariantCulture));
|
||||
context.MarkAsWritten(value, id);
|
||||
context.MarkAsWrittenForIId(value, metadata, id);
|
||||
}
|
||||
|
||||
var wrapper = context.GetWrapper(type);
|
||||
var metadata = wrapper.Metadata;
|
||||
var props = metadata.Properties;
|
||||
var propCount = props.Length;
|
||||
var nextDepth = depth + 1;
|
||||
|
|
|
|||
Loading…
Reference in New Issue