AyCode.Core/AyCode.Core/Serializers/PropertyMetadataBase.cs

156 lines
4.9 KiB
C#

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using AyCode.Core.Serializers.Binaries;
using static AyCode.Core.Helpers.JsonUtilities;
namespace AyCode.Core.Serializers;
/// <summary>
/// Enum for typed property accessor dispatch.
/// </summary>
public enum PropertyAccessorType : byte
{
Object = 0,
Int32,
Int64,
Boolean,
Double,
Single,
Decimal,
DateTime,
Byte,
Int16,
UInt16,
UInt32,
UInt64,
Guid,
Enum,
String
}
/// <summary>
/// Base class containing common property metadata shared by serializers and deserializers.
/// Contains the dynamic getter used by both serialize (for reading) and deserialize (for Populate/Merge).
/// </summary>
public abstract class PropertyMetadataBase
{
/// <summary>
/// Property name.
/// </summary>
public string Name { get; }
/// <summary>
/// Pre-encoded UTF8 bytes of property name for fast matching.
/// </summary>
public byte[] NameUtf8 { get; }
/// <summary>
/// The property type (may be nullable).
/// </summary>
public Type PropertyType { get; }
/// <summary>
/// The underlying type (unwrapped from Nullable if applicable).
/// </summary>
public Type UnderlyingType { get; }
/// <summary>
/// Cached TypeCode for fast primitive type dispatch.
/// </summary>
public TypeCode PropertyTypeCode { get; }
/// <summary>
/// Whether the property type is nullable.
/// </summary>
public bool IsNullable { get; }
/// <summary>
/// The declaring type of this property.
/// </summary>
public Type DeclaringType { get; }
/// <summary>
/// True if this property needs recursive scanning (not primitive/string).
/// Pre-computed to avoid IsPrimitiveOrStringFast() calls in hot path.
/// </summary>
public bool IsComplexType { get; }
/// <summary>
/// FNV-1a hash of property name. Deterministic across processes.
/// Used for property matching in UseMetadata mode.
/// </summary>
public int PropertyNameHash { get; }
/// <summary>
/// The accessor type for fast typed getter/setter dispatch.
/// </summary>
public PropertyAccessorType AccessorType { get; }
/// <summary>
/// Compiled getter delegate for reading property values (boxed).
/// Used by serialize (for reading values) and deserialize (for Populate/Merge to get existing references).
/// </summary>
protected readonly Func<object, object?> _dynamicGetter;
protected PropertyMetadataBase(PropertyInfo prop, Type declaringType)
{
Name = prop.Name;
NameUtf8 = Encoding.UTF8.GetBytes(prop.Name);
DeclaringType = declaringType;
PropertyType = prop.PropertyType;
var underlying = Nullable.GetUnderlyingType(PropertyType);
IsNullable = underlying != null;
UnderlyingType = underlying ?? PropertyType;
PropertyTypeCode = Type.GetTypeCode(UnderlyingType);
// Pre-compute: is this a complex type that needs recursive handling?
IsComplexType = !IsPrimitiveOrStringFast(PropertyType);
PropertyNameHash = FnvHash.ComputeString(Name);
AccessorType = DetermineAccessorType(PropertyType);
_dynamicGetter = AcSerializerCommon.CreateCompiledGetter(declaringType, prop);
}
/// <summary>
/// Gets the property value from the target object (boxed).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object? GetValue(object obj) => _dynamicGetter(obj);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static PropertyAccessorType DetermineAccessorType(Type propType)
{
var underlying = Nullable.GetUnderlyingType(propType);
if (underlying != null)
return PropertyAccessorType.Object;
if (propType.IsEnum)
return PropertyAccessorType.Enum;
if (ReferenceEquals(propType, GuidType))
return PropertyAccessorType.Guid;
return Type.GetTypeCode(propType) switch
{
TypeCode.Int32 => PropertyAccessorType.Int32,
TypeCode.Int64 => PropertyAccessorType.Int64,
TypeCode.Boolean => PropertyAccessorType.Boolean,
TypeCode.Double => PropertyAccessorType.Double,
TypeCode.Single => PropertyAccessorType.Single,
TypeCode.Decimal => PropertyAccessorType.Decimal,
TypeCode.DateTime => PropertyAccessorType.DateTime,
TypeCode.Byte => PropertyAccessorType.Byte,
TypeCode.Int16 => PropertyAccessorType.Int16,
TypeCode.UInt16 => PropertyAccessorType.UInt16,
TypeCode.UInt32 => PropertyAccessorType.UInt32,
TypeCode.UInt64 => PropertyAccessorType.UInt64,
TypeCode.String => PropertyAccessorType.String,
_ => PropertyAccessorType.Object
};
}
}