780 lines
35 KiB
C#
780 lines
35 KiB
C#
//using System;
|
|
//using System.Collections.Generic;
|
|
//using System.Collections.Immutable;
|
|
//using System.Linq;
|
|
//using System.Text;
|
|
//using Microsoft.CodeAnalysis;
|
|
//using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
//using Microsoft.CodeAnalysis.Text;
|
|
|
|
//namespace AyCode.Core.Serializers.SourceGenerator;
|
|
|
|
///// <summary>
|
|
///// Source Generator for AcBinary serialization.
|
|
///// Generates optimized serialize/deserialize methods for classes marked with [AcBinarySerializable].
|
|
///// </summary>
|
|
//[Generator]
|
|
//public class AcBinarySourceGenerator : IIncrementalGenerator
|
|
//{
|
|
// private const string AcBinarySerializableAttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute";
|
|
|
|
// public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
// {
|
|
// // Find all classes with [AcBinarySerializable] attribute
|
|
// var classDeclarations = context.SyntaxProvider
|
|
// .ForAttributeWithMetadataName(
|
|
// AcBinarySerializableAttributeName,
|
|
// predicate: static (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax,
|
|
// transform: static (ctx, _) => GetClassInfo(ctx))
|
|
// .Where(static info => info != null);
|
|
|
|
// // Combine with compilation
|
|
// var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
|
|
|
|
// // Generate source
|
|
// context.RegisterSourceOutput(compilationAndClasses,
|
|
// static (spc, source) => Execute(source.Left, source.Right, spc));
|
|
// }
|
|
|
|
// private static SerializableClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
|
|
// {
|
|
// if (!(context.TargetSymbol is INamedTypeSymbol typeSymbol))
|
|
// return null;
|
|
|
|
// var namespaceName = typeSymbol.ContainingNamespace.IsGlobalNamespace
|
|
// ? string.Empty
|
|
// : typeSymbol.ContainingNamespace.ToDisplayString();
|
|
|
|
// var className = typeSymbol.Name;
|
|
// var fullTypeName = typeSymbol.ToDisplayString();
|
|
// var isStruct = typeSymbol.IsValueType;
|
|
|
|
// // Check if this is a nested type
|
|
// var isNestedType = typeSymbol.ContainingType != null;
|
|
|
|
// // For nested types, we need the full containing type path for method signatures
|
|
// // e.g. "OuterClass.InnerClass" instead of just "InnerClass"
|
|
// var typeNameForSignature = isNestedType
|
|
// ? GetNestedTypeName(typeSymbol)
|
|
// : className;
|
|
|
|
// // Get all public properties with getter and setter
|
|
// // DUPLICATED LOGIC: Same filtering as TypeMetadataBase.GetSerializableProperties()
|
|
// var properties = new List<PropertyInfo>();
|
|
// foreach (var member in typeSymbol.GetMembers())
|
|
// {
|
|
// if (member is IPropertySymbol p &&
|
|
// p.DeclaredAccessibility == Accessibility.Public &&
|
|
// p.GetMethod != null &&
|
|
// p.SetMethod != null &&
|
|
// !p.IsIndexer &&
|
|
// !p.IsStatic)
|
|
// {
|
|
// properties.Add(new PropertyInfo(
|
|
// p.Name,
|
|
// p.Type.ToDisplayString(),
|
|
// GetPropertyTypeKind(p.Type),
|
|
// p.Type.NullableAnnotation == NullableAnnotation.Annotated || IsNullableValueType(p.Type)));
|
|
// }
|
|
// }
|
|
|
|
// // DUPLICATED LOGIC: Same ordering as TypeMetadataBase.GetSerializableProperties()
|
|
// properties.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
|
|
|
// return new SerializableClassInfo(namespaceName, className, fullTypeName, isStruct, isNestedType, typeNameForSignature, properties);
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// Gets the nested type name chain (e.g., "OuterClass.MiddleClass.InnerClass")
|
|
// /// </summary>
|
|
// private static string GetNestedTypeName(INamedTypeSymbol typeSymbol)
|
|
// {
|
|
// var parts = new List<string>();
|
|
// var current = typeSymbol;
|
|
|
|
// while (current != null)
|
|
// {
|
|
// parts.Insert(0, current.Name);
|
|
// current = current.ContainingType;
|
|
// }
|
|
|
|
// return string.Join(".", parts);
|
|
// }
|
|
|
|
// private static bool IsNullableValueType(ITypeSymbol type)
|
|
// {
|
|
// return type is INamedTypeSymbol namedType &&
|
|
// namedType.IsGenericType &&
|
|
// namedType.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T;
|
|
// }
|
|
|
|
// private static PropertyTypeKind GetPropertyTypeKind(ITypeSymbol type)
|
|
// {
|
|
// // Handle nullable value types
|
|
// if (type is INamedTypeSymbol namedType && namedType.IsGenericType &&
|
|
// namedType.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
|
|
// {
|
|
// // Get underlying type
|
|
// var underlyingType = namedType.TypeArguments[0];
|
|
// return GetPropertyTypeKindForUnderlying(underlyingType, isNullable: true);
|
|
// }
|
|
|
|
// return GetPropertyTypeKindForUnderlying(type, isNullable: false);
|
|
// }
|
|
|
|
// private static PropertyTypeKind GetPropertyTypeKindForUnderlying(ITypeSymbol type, bool isNullable)
|
|
// {
|
|
// switch (type.SpecialType)
|
|
// {
|
|
// case SpecialType.System_String: return PropertyTypeKind.String;
|
|
// case SpecialType.System_Int32: return isNullable ? PropertyTypeKind.NullableInt32 : PropertyTypeKind.Int32;
|
|
// case SpecialType.System_Int64: return isNullable ? PropertyTypeKind.NullableInt64 : PropertyTypeKind.Int64;
|
|
// case SpecialType.System_Int16: return isNullable ? PropertyTypeKind.NullableInt16 : PropertyTypeKind.Int16;
|
|
// case SpecialType.System_Byte: return isNullable ? PropertyTypeKind.NullableByte : PropertyTypeKind.Byte;
|
|
// case SpecialType.System_SByte: return isNullable ? PropertyTypeKind.NullableSByte : PropertyTypeKind.SByte;
|
|
// case SpecialType.System_UInt16: return isNullable ? PropertyTypeKind.NullableUInt16 : PropertyTypeKind.UInt16;
|
|
// case SpecialType.System_UInt32: return isNullable ? PropertyTypeKind.NullableUInt32 : PropertyTypeKind.UInt32;
|
|
// case SpecialType.System_UInt64: return isNullable ? PropertyTypeKind.NullableUInt64 : PropertyTypeKind.UInt64;
|
|
// case SpecialType.System_Boolean: return isNullable ? PropertyTypeKind.NullableBoolean : PropertyTypeKind.Boolean;
|
|
// case SpecialType.System_Single: return isNullable ? PropertyTypeKind.NullableSingle : PropertyTypeKind.Single;
|
|
// case SpecialType.System_Double: return isNullable ? PropertyTypeKind.NullableDouble : PropertyTypeKind.Double;
|
|
// case SpecialType.System_Decimal: return isNullable ? PropertyTypeKind.NullableDecimal : PropertyTypeKind.Decimal;
|
|
// case SpecialType.System_DateTime: return isNullable ? PropertyTypeKind.NullableDateTime : PropertyTypeKind.DateTime;
|
|
// default: return GetNonSpecialTypeKind(type, isNullable);
|
|
// }
|
|
// }
|
|
|
|
// private static PropertyTypeKind GetNonSpecialTypeKind(ITypeSymbol type, bool isNullable)
|
|
// {
|
|
// var fullName = type.ToDisplayString();
|
|
|
|
// if (fullName == "System.Guid") return isNullable ? PropertyTypeKind.NullableGuid : PropertyTypeKind.Guid;
|
|
// if (fullName == "System.TimeSpan") return isNullable ? PropertyTypeKind.NullableTimeSpan : PropertyTypeKind.TimeSpan;
|
|
// if (fullName == "System.DateTimeOffset") return isNullable ? PropertyTypeKind.NullableDateTimeOffset : PropertyTypeKind.DateTimeOffset;
|
|
// if (fullName == "System.DateOnly") return isNullable ? PropertyTypeKind.NullableDateOnly : PropertyTypeKind.DateOnly;
|
|
// if (fullName == "System.TimeOnly") return isNullable ? PropertyTypeKind.NullableTimeOnly : PropertyTypeKind.TimeOnly;
|
|
// if (type.TypeKind == TypeKind.Enum) return isNullable ? PropertyTypeKind.NullableEnum : PropertyTypeKind.Enum;
|
|
// if (IsCollectionType(type)) return PropertyTypeKind.Collection;
|
|
// if (type.TypeKind == TypeKind.Class || type.TypeKind == TypeKind.Struct) return PropertyTypeKind.Complex;
|
|
|
|
// return PropertyTypeKind.Unknown;
|
|
// }
|
|
|
|
// private static bool IsCollectionType(ITypeSymbol type)
|
|
// {
|
|
// if (type is IArrayTypeSymbol)
|
|
// return true;
|
|
|
|
// if (type is INamedTypeSymbol namedType)
|
|
// {
|
|
// foreach (var iface in namedType.AllInterfaces)
|
|
// {
|
|
// if (iface.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)
|
|
// return true;
|
|
// var ifaceName = iface.ToDisplayString();
|
|
// if (ifaceName.StartsWith("System.Collections.Generic.IList<") ||
|
|
// ifaceName.StartsWith("System.Collections.Generic.ICollection<"))
|
|
// return true;
|
|
// }
|
|
// }
|
|
|
|
// return false;
|
|
// }
|
|
|
|
// private static void Execute(Compilation compilation, ImmutableArray<SerializableClassInfo> classes, SourceProductionContext context)
|
|
// {
|
|
// if (classes.IsDefaultOrEmpty)
|
|
// return;
|
|
|
|
// foreach (var classInfo in classes)
|
|
// {
|
|
// if (classInfo == null)
|
|
// continue;
|
|
|
|
// var source = GenerateSerializerClass(classInfo);
|
|
// context.AddSource($"{classInfo.ClassName}_AcBinarySerializer.g.cs", SourceText.From(source, Encoding.UTF8));
|
|
// }
|
|
// }
|
|
|
|
// private static string GenerateSerializerClass(SerializableClassInfo classInfo)
|
|
// {
|
|
// var sb = new StringBuilder();
|
|
|
|
// sb.AppendLine("// <auto-generated/>");
|
|
// sb.AppendLine("#nullable enable");
|
|
// sb.AppendLine();
|
|
// sb.AppendLine("using System;");
|
|
// sb.AppendLine("using System.Runtime.CompilerServices;");
|
|
// sb.AppendLine("using AyCode.Core.Serializers.Binaries;");
|
|
// sb.AppendLine();
|
|
|
|
// if (!string.IsNullOrEmpty(classInfo.Namespace))
|
|
// {
|
|
// sb.AppendLine($"namespace {classInfo.Namespace}");
|
|
// sb.AppendLine("{");
|
|
// }
|
|
|
|
// var indent = string.IsNullOrEmpty(classInfo.Namespace) ? "" : " ";
|
|
|
|
// sb.AppendLine($"{indent}/// <summary>");
|
|
// sb.AppendLine($"{indent}/// Generated binary serializer for {classInfo.ClassName}.");
|
|
// sb.AppendLine($"{indent}/// </summary>");
|
|
// sb.AppendLine($"{indent}internal static class {classInfo.ClassName}_AcBinarySerializer");
|
|
// sb.AppendLine($"{indent}{{");
|
|
|
|
// // Generate property count constant
|
|
// sb.AppendLine($"{indent} public const int PropertyCount = {classInfo.Properties.Count};");
|
|
// sb.AppendLine();
|
|
|
|
// // Generate property names array for validation
|
|
// sb.AppendLine($"{indent} /// <summary>");
|
|
// sb.AppendLine($"{indent} /// Property names in serialization order (alphabetical).");
|
|
// sb.AppendLine($"{indent} /// Used for runtime validation against TypeMetadataBase.GetSerializableProperties().");
|
|
// sb.AppendLine($"{indent} /// </summary>");
|
|
// sb.Append($"{indent} public static readonly string[] PropertyNames = new[] {{ ");
|
|
// sb.Append(string.Join(", ", classInfo.Properties.Select(p => $"\"{p.Name}\"")));
|
|
// sb.AppendLine(" };");
|
|
// sb.AppendLine();
|
|
|
|
// // Generate Serialize method
|
|
// GenerateSerializeMethod(sb, classInfo, indent);
|
|
|
|
// // Generate Deserialize method
|
|
// GenerateDeserializeMethod(sb, classInfo, indent);
|
|
|
|
// sb.AppendLine($"{indent}}}");
|
|
|
|
// if (!string.IsNullOrEmpty(classInfo.Namespace))
|
|
// {
|
|
// sb.AppendLine("}");
|
|
// }
|
|
|
|
// return sb.ToString();
|
|
// }
|
|
|
|
// private static void GenerateSerializeMethod(StringBuilder sb, SerializableClassInfo classInfo, string indent)
|
|
// {
|
|
// sb.AppendLine($"{indent} /// <summary>");
|
|
// sb.AppendLine($"{indent} /// Serializes a {classInfo.ClassName} instance to the binary context.");
|
|
// sb.AppendLine($"{indent} /// Direct property access - no reflection, no boxing for primitives.");
|
|
// sb.AppendLine($"{indent} /// </summary>");
|
|
// sb.AppendLine($"{indent} [MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
|
// sb.AppendLine($"{indent} public static void Serialize<TOutput>({classInfo.TypeNameForSignature} obj, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : BinaryOutputBase");
|
|
// sb.AppendLine($"{indent} {{");
|
|
// sb.AppendLine($"{indent} var output = context.Output;");
|
|
|
|
// foreach (var prop in classInfo.Properties)
|
|
// {
|
|
// GenerateSerializeProperty(sb, prop, indent + " ");
|
|
// }
|
|
|
|
// sb.AppendLine($"{indent} }}");
|
|
// sb.AppendLine();
|
|
// }
|
|
|
|
// private static void GenerateSerializeProperty(StringBuilder sb, PropertyInfo prop, string indent)
|
|
// {
|
|
// var propAccess = $"obj.{prop.Name}";
|
|
|
|
// // Handle nullable VALUE types (Nullable<T>) - these use .HasValue and .Value
|
|
// if (IsNullableValueTypeKind(prop.TypeKind))
|
|
// {
|
|
// sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName} (nullable value type)");
|
|
// sb.AppendLine($"{indent}if ({propAccess}.HasValue)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// GenerateSerializeValue(sb, prop.TypeKind, $"{propAccess}.Value", indent + " ");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.Null);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// return;
|
|
// }
|
|
|
|
// sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
|
|
|
|
// // String needs null check
|
|
// if (prop.TypeKind == PropertyTypeKind.String)
|
|
// {
|
|
// sb.AppendLine($"{indent}if ({propAccess} == null)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.Null);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if ({propAccess}.Length == 0)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.StringEmpty);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.String);");
|
|
// sb.AppendLine($"{indent} context.WriteStringUtf8({propAccess});");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// return;
|
|
// }
|
|
|
|
// // Nullable reference types (Complex/Collection with ? annotation) - use == null
|
|
// if (prop.IsNullable && (prop.TypeKind == PropertyTypeKind.Complex || prop.TypeKind == PropertyTypeKind.Collection))
|
|
// {
|
|
// sb.AppendLine($"{indent}if ({propAccess} == null)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.Null);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// GenerateSerializeValue(sb, prop.TypeKind, propAccess, indent + " ");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// return;
|
|
// }
|
|
|
|
// GenerateSerializeValue(sb, prop.TypeKind, propAccess, indent);
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// Checks if the type kind represents a nullable VALUE type (Nullable<T>), not a reference type
|
|
// /// </summary>
|
|
// private static bool IsNullableValueTypeKind(PropertyTypeKind kind)
|
|
// {
|
|
// return kind >= PropertyTypeKind.NullableInt32 && kind <= PropertyTypeKind.NullableEnum;
|
|
// }
|
|
|
|
// private static void GenerateSerializeValue(StringBuilder sb, PropertyTypeKind typeKind, string valueExpr, string indent)
|
|
// {
|
|
// switch (typeKind)
|
|
// {
|
|
// case PropertyTypeKind.Int32:
|
|
// case PropertyTypeKind.NullableInt32:
|
|
// sb.AppendLine($"{indent}if (BinaryTypeCode.TryEncodeTinyInt({valueExpr}, out var tiny_{valueExpr.Replace(".", "_")}))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(tiny_{valueExpr.Replace(".", "_")});");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.Int32);");
|
|
// sb.AppendLine($"{indent} context.WriteVarInt({valueExpr});");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Int64:
|
|
// case PropertyTypeKind.NullableInt64:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.Int64);");
|
|
// sb.AppendLine($"{indent}context.WriteVarLong({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Boolean:
|
|
// case PropertyTypeKind.NullableBoolean:
|
|
// sb.AppendLine($"{indent}context.WriteByte({valueExpr} ? BinaryTypeCode.True : BinaryTypeCode.False);");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Double:
|
|
// case PropertyTypeKind.NullableDouble:
|
|
// sb.AppendLine($"{indent}context.WriteTypeCodeAndRaw(BinaryTypeCode.Float64, {valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Single:
|
|
// case PropertyTypeKind.NullableSingle:
|
|
// sb.AppendLine($"{indent}context.WriteTypeCodeAndRaw(BinaryTypeCode.Float32, {valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Decimal:
|
|
// case PropertyTypeKind.NullableDecimal:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.Decimal);");
|
|
// sb.AppendLine($"{indent}context.WriteDecimalBits({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.DateTime:
|
|
// case PropertyTypeKind.NullableDateTime:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.DateTime);");
|
|
// sb.AppendLine($"{indent}context.WriteDateTimeBits({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Guid:
|
|
// case PropertyTypeKind.NullableGuid:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.Guid);");
|
|
// sb.AppendLine($"{indent}context.WriteGuidBits({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Byte:
|
|
// case PropertyTypeKind.NullableByte:
|
|
// sb.AppendLine($"{indent}context.WriteTwoBytes(BinaryTypeCode.UInt8, {valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Int16:
|
|
// case PropertyTypeKind.NullableInt16:
|
|
// sb.AppendLine($"{indent}context.WriteTypeCodeAndRaw(BinaryTypeCode.Int16, {valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt16:
|
|
// case PropertyTypeKind.NullableUInt16:
|
|
// sb.AppendLine($"{indent}context.WriteTypeCodeAndRaw(BinaryTypeCode.UInt16, {valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt32:
|
|
// case PropertyTypeKind.NullableUInt32:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.UInt32);");
|
|
// sb.AppendLine($"{indent}context.WriteVarUInt({valueExpr});");
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.UInt32);");
|
|
// sb.AppendLine($"{indent}context.WriteVarUInt({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt64:
|
|
// case PropertyTypeKind.NullableUInt64:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.UInt64);");
|
|
// sb.AppendLine($"{indent}context.WriteVarULong({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.TimeSpan:
|
|
// case PropertyTypeKind.NullableTimeSpan:
|
|
// sb.AppendLine($"{indent}context.WriteTypeCodeAndRaw(BinaryTypeCode.TimeSpan, {valueExpr}.Ticks);");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.DateTimeOffset:
|
|
// case PropertyTypeKind.NullableDateTimeOffset:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.DateTimeOffset);");
|
|
// sb.AppendLine($"{indent}context.WriteDateTimeOffsetBits({valueExpr});");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Enum:
|
|
// case PropertyTypeKind.NullableEnum:
|
|
// sb.AppendLine($"{indent}context.WriteByte(BinaryTypeCode.Enum);");
|
|
// sb.AppendLine($"{indent}var enumVal = (int){valueExpr};");
|
|
// sb.AppendLine($"{indent}if (BinaryTypeCode.TryEncodeTinyInt(enumVal, out var tinyEnum))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(tinyEnum);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.WriteByte(BinaryTypeCode.Int32);");
|
|
// sb.AppendLine($"{indent} context.WriteVarInt(enumVal);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Collection:
|
|
// case PropertyTypeKind.Complex:
|
|
// // TODO: Collections and complex types will be implemented later
|
|
// sb.AppendLine($"{indent}// TODO: Complex/Collection type - fallback to runtime serializer");
|
|
// sb.AppendLine($"{indent}throw new NotImplementedException(\"Complex/Collection types not yet implemented in generated serializer\");");
|
|
// break;
|
|
|
|
// default:
|
|
// sb.AppendLine($"{indent}// Unknown type - fallback needed");
|
|
// sb.AppendLine($"{indent}throw new NotImplementedException($\"Type {typeKind} not implemented in generated serializer\");");
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// private static void GenerateDeserializeMethod(StringBuilder sb, SerializableClassInfo classInfo, string indent)
|
|
// {
|
|
// sb.AppendLine($"{indent} /// <summary>");
|
|
// sb.AppendLine($"{indent} /// Deserializes properties into a {classInfo.ClassName} instance from the binary context.");
|
|
// sb.AppendLine($"{indent} /// Direct property access - no reflection, no boxing for primitives.");
|
|
// sb.AppendLine($"{indent} /// </summary>");
|
|
// sb.AppendLine($"{indent} [MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
|
// sb.AppendLine($"{indent} public static void Deserialize({classInfo.TypeNameForSignature} obj, ref AcBinaryDeserializer.BinaryDeserializationContext context)");
|
|
// sb.AppendLine($"{indent} {{");
|
|
|
|
// foreach (var prop in classInfo.Properties)
|
|
// {
|
|
// GenerateDeserializeProperty(sb, prop, indent + " ");
|
|
// }
|
|
|
|
// sb.AppendLine($"{indent} }}");
|
|
// }
|
|
|
|
// private static void GenerateDeserializeProperty(StringBuilder sb, PropertyInfo prop, string indent)
|
|
// {
|
|
// sb.AppendLine($"{indent}// {prop.Name}: {prop.TypeName}");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} var peekCode = context.PeekByte();");
|
|
|
|
// // Handle Skip marker
|
|
// sb.AppendLine($"{indent} if (peekCode == BinaryTypeCode.PropertySkip)");
|
|
// sb.AppendLine($"{indent} {{");
|
|
// sb.AppendLine($"{indent} context.ReadByte(); // consume Skip marker");
|
|
// sb.AppendLine($"{indent} // Property keeps default value");
|
|
// sb.AppendLine($"{indent} }}");
|
|
|
|
// // Handle Null marker
|
|
// sb.AppendLine($"{indent} else if (peekCode == BinaryTypeCode.Null)");
|
|
// sb.AppendLine($"{indent} {{");
|
|
// sb.AppendLine($"{indent} context.ReadByte(); // consume Null marker");
|
|
// if (prop.IsNullable || prop.TypeKind == PropertyTypeKind.String)
|
|
// {
|
|
// sb.AppendLine($"{indent} obj.{prop.Name} = default;");
|
|
// }
|
|
// else
|
|
// {
|
|
// sb.AppendLine($"{indent} // Non-nullable property, keep default");
|
|
// }
|
|
// sb.AppendLine($"{indent} }}");
|
|
|
|
// // Handle actual value
|
|
// sb.AppendLine($"{indent} else");
|
|
// sb.AppendLine($"{indent} {{");
|
|
// GenerateDeserializeValue(sb, prop, indent + " ");
|
|
// sb.AppendLine($"{indent} }}");
|
|
|
|
// sb.AppendLine($"{indent}}}");
|
|
// }
|
|
|
|
// private static void GenerateDeserializeValue(StringBuilder sb, PropertyInfo prop, string indent)
|
|
// {
|
|
// var propAccess = $"obj.{prop.Name}";
|
|
|
|
// switch (prop.TypeKind)
|
|
// {
|
|
// case PropertyTypeKind.String:
|
|
// sb.AppendLine($"{indent}if (peekCode == BinaryTypeCode.StringEmpty)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = string.Empty;");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if (peekCode == BinaryTypeCode.String)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} var len = (int)context.ReadVarUInt();");
|
|
// sb.AppendLine($"{indent} {propAccess} = context.ReadStringUtf8(len);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if (BinaryTypeCode.IsFixStr(peekCode))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} var len = BinaryTypeCode.DecodeFixStrLength(peekCode);");
|
|
// sb.AppendLine($"{indent} {propAccess} = len == 0 ? string.Empty : context.ReadStringUtf8(len);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Int32:
|
|
// case PropertyTypeKind.NullableInt32:
|
|
// sb.AppendLine($"{indent}if (BinaryTypeCode.IsTinyInt(peekCode))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = BinaryTypeCode.DecodeTinyInt(peekCode);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if (peekCode == BinaryTypeCode.Int32)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = context.ReadVarInt();");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Int64:
|
|
// case PropertyTypeKind.NullableInt64:
|
|
// sb.AppendLine($"{indent}if (BinaryTypeCode.IsTinyInt(peekCode))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = BinaryTypeCode.DecodeTinyInt(peekCode);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if (peekCode == BinaryTypeCode.Int32)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = context.ReadVarInt();");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else if (peekCode == BinaryTypeCode.Int64)");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = context.ReadVarLong();");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Boolean:
|
|
// case PropertyTypeKind.NullableBoolean:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = peekCode == BinaryTypeCode.True;");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Double:
|
|
// case PropertyTypeKind.NullableDouble:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadDoubleUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Single:
|
|
// case PropertyTypeKind.NullableSingle:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadSingleUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Decimal:
|
|
// case PropertyTypeKind.NullableDecimal:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadDecimalUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.DateTime:
|
|
// case PropertyTypeKind.NullableDateTime:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadDateTimeUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Guid:
|
|
// case PropertyTypeKind.NullableGuid:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadGuidUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Byte:
|
|
// case PropertyTypeKind.NullableByte:
|
|
// sb.AppendLine($"{indent}if (BinaryTypeCode.IsTinyInt(peekCode))");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = (byte)BinaryTypeCode.DecodeTinyInt(peekCode);");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// sb.AppendLine($"{indent}else");
|
|
// sb.AppendLine($"{indent}{{");
|
|
// sb.AppendLine($"{indent} context.ReadByte();");
|
|
// sb.AppendLine($"{indent} {propAccess} = context.ReadByte();");
|
|
// sb.AppendLine($"{indent}}}");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Int16:
|
|
// case PropertyTypeKind.NullableInt16:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadInt16Unsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt16:
|
|
// case PropertyTypeKind.NullableUInt16:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadUInt16Unsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt32:
|
|
// case PropertyTypeKind.NullableUInt32:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadVarUInt();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.UInt64:
|
|
// case PropertyTypeKind.NullableUInt64:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadVarULong();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.TimeSpan:
|
|
// case PropertyTypeKind.NullableTimeSpan:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadTimeSpanUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.DateTimeOffset:
|
|
// case PropertyTypeKind.NullableDateTimeOffset:
|
|
// sb.AppendLine($"{indent}context.ReadByte();");
|
|
// sb.AppendLine($"{indent}{propAccess} = context.ReadDateTimeOffsetUnsafe();");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Enum:
|
|
// case PropertyTypeKind.NullableEnum:
|
|
// sb.AppendLine($"{indent}// TODO: Enum deserialization needs type info");
|
|
// sb.AppendLine($"{indent}throw new NotImplementedException(\"Enum deserialization not yet implemented\");");
|
|
// break;
|
|
|
|
// case PropertyTypeKind.Collection:
|
|
// case PropertyTypeKind.Complex:
|
|
// sb.AppendLine($"{indent}// TODO: Complex/Collection types");
|
|
// sb.AppendLine($"{indent}throw new NotImplementedException(\"Complex/Collection deserialization not yet implemented\");");
|
|
// break;
|
|
|
|
// default:
|
|
// sb.AppendLine($"{indent}throw new NotImplementedException($\"Type deserialization not implemented\");");
|
|
// break;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
///// <summary>
|
|
///// Information about a class marked with [AcBinarySerializable].
|
|
///// </summary>
|
|
//internal sealed class SerializableClassInfo
|
|
//{
|
|
// public string Namespace { get; }
|
|
// public string ClassName { get; }
|
|
// public string FullTypeName { get; }
|
|
// public bool IsStruct { get; }
|
|
// public bool IsNestedType { get; }
|
|
// /// <summary>
|
|
// /// The type name to use in method signatures. For nested types this includes the containing types.
|
|
// /// e.g., "OuterClass.InnerClass"
|
|
// /// </summary>
|
|
// public string TypeNameForSignature { get; }
|
|
// public List<PropertyInfo> Properties { get; }
|
|
|
|
// public SerializableClassInfo(string ns, string className, string fullTypeName, bool isStruct, bool isNestedType, string typeNameForSignature, List<PropertyInfo> properties)
|
|
// {
|
|
// Namespace = ns;
|
|
// ClassName = className;
|
|
// FullTypeName = fullTypeName;
|
|
// IsStruct = isStruct;
|
|
// IsNestedType = isNestedType;
|
|
// TypeNameForSignature = typeNameForSignature;
|
|
// Properties = properties;
|
|
// }
|
|
//}
|
|
|
|
///// <summary>
|
|
///// Information about a serializable property.
|
|
///// </summary>
|
|
//internal sealed class PropertyInfo
|
|
//{
|
|
// public string Name { get; }
|
|
// public string TypeName { get; }
|
|
// public PropertyTypeKind TypeKind { get; }
|
|
// public bool IsNullable { get; }
|
|
|
|
// public PropertyInfo(string name, string typeName, PropertyTypeKind typeKind, bool isNullable)
|
|
// {
|
|
// Name = name;
|
|
// TypeName = typeName;
|
|
// TypeKind = typeKind;
|
|
// IsNullable = isNullable;
|
|
// }
|
|
//}
|
|
|
|
///// <summary>
|
|
///// Kind of property type for code generation.
|
|
///// </summary>
|
|
//internal enum PropertyTypeKind
|
|
//{
|
|
// Unknown,
|
|
// String,
|
|
// Int32,
|
|
// Int64,
|
|
// Int16,
|
|
// Byte,
|
|
// SByte,
|
|
// UInt16,
|
|
// UInt32,
|
|
// UInt64,
|
|
// Boolean,
|
|
// Single,
|
|
// Double,
|
|
// Decimal,
|
|
// DateTime,
|
|
// DateTimeOffset,
|
|
// TimeSpan,
|
|
// DateOnly,
|
|
// TimeOnly,
|
|
// Guid,
|
|
// Enum,
|
|
// Collection,
|
|
// Complex,
|
|
// // Nullable variants
|
|
// NullableInt32,
|
|
// NullableInt64,
|
|
// NullableInt16,
|
|
// NullableByte,
|
|
// NullableSByte,
|
|
// NullableUInt16,
|
|
// NullableUInt32,
|
|
// NullableUInt64,
|
|
// NullableBoolean,
|
|
// NullableSingle,
|
|
// NullableDouble,
|
|
// NullableDecimal,
|
|
// NullableDateTime,
|
|
// NullableDateTimeOffset,
|
|
// NullableTimeSpan,
|
|
// NullableDateOnly,
|
|
// NullableTimeOnly,
|
|
// NullableGuid,
|
|
// NullableEnum
|
|
//}
|