156 lines
4.9 KiB
C#
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
|
|
};
|
|
}
|
|
}
|