diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index a3e4284..2fd71d1 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -31,7 +31,8 @@
"Bash(Remove-Item \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Core\\\\Serializers\\\\Toons\\\\AcToonSerializer.RelationshipDetection.cs\")",
"Bash(find:*)",
"Bash(dir:*)",
- "Bash(git stash:*)"
+ "Bash(git stash:*)",
+ "WebFetch(domain:github.com)"
]
}
}
diff --git a/AyCode.Core.Serializers.Console/Program.cs b/AyCode.Core.Serializers.Console/Program.cs
index 4da051a..38052b5 100644
--- a/AyCode.Core.Serializers.Console/Program.cs
+++ b/AyCode.Core.Serializers.Console/Program.cs
@@ -51,6 +51,9 @@ public static class Program
#else
private static int WarmupIterations = 2000;
private static int TestIterations = 1000;
+
+ //private static int WarmupIterations = 5000;
+ //private static int TestIterations = 2000;
#endif
public static void Main(string[] args)
@@ -209,11 +212,16 @@ public static class Program
{
// AcBinary variants
- new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.Default, SerializerAcBinaryDefault),
- new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.WithoutReferenceHandling, SerializerAcBinaryNoRef),
+ //new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.Default, SerializerAcBinaryDefault),
+ //new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.WithoutReferenceHandling, SerializerAcBinaryNoRef),
+ //new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryFastMode),
+ //new AcBinaryBenchmark(testData.Order, new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None }, SerializerAcBinaryNoIntern),
+
+ new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryDefault),
+ new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryNoRef),
new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryFastMode),
- new AcBinaryBenchmark(testData.Order, new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None }, SerializerAcBinaryNoIntern),
-
+ new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryNoIntern),
+
// AcJson
new AcJsonBenchmark(testData.Order, AcJsonSerializerOptions.Default, SerializerAcJsonDefault),
@@ -550,8 +558,8 @@ public static class Program
}
System.Console.WriteLine($"└{"─".PadRight(6, '─')}─{"─".PadRight(27, '─')}┴{"─".PadRight(12, '─')}┴{"─".PadRight(14, '─')}┴{"─".PadRight(14, '─')}┴{"─".PadRight(13, '─')}┘");
- System.Console.WriteLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
- System.Console.WriteLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
+ //System.Console.WriteLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
+ //System.Console.WriteLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
}
// Summary: Best serializer for each category
@@ -751,8 +759,8 @@ public static class Program
sb.AppendLine($" {SerializerAcBinaryDefault} vs {SerializerMessagePack}: Size {sizePct:+0;-0}% │ Ser {serPct:+0;-0}% │ Des {desPct:+0;-0}% │ RT {rtPct:+0;-0}%");
}
- sb.AppendLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
- sb.AppendLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
+ //sb.AppendLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
+ //sb.AppendLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
}
diff --git a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs
index 839dd44..4d88643 100644
--- a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs
+++ b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs
@@ -1,778 +1,779 @@
-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;
+//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;
+//namespace AyCode.Core.Serializers.SourceGenerator;
-///
-/// Source Generator for AcBinary serialization.
-/// Generates optimized serialize/deserialize methods for classes marked with [AcBinarySerializable].
-///
-[Generator]
-public class AcBinarySourceGenerator : IIncrementalGenerator
-{
- private const string AcBinarySerializableAttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute";
+/////
+///// Source Generator for AcBinary serialization.
+///// Generates optimized serialize/deserialize methods for classes marked with [AcBinarySerializable].
+/////
+//[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);
+// 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());
+// // Combine with compilation
+// var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
- // Generate source
- context.RegisterSourceOutput(compilationAndClasses,
- static (spc, source) => Execute(source.Left, source.Right, spc));
- }
+// // 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;
+// 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 namespaceName = typeSymbol.ContainingNamespace.IsGlobalNamespace
+// ? string.Empty
+// : typeSymbol.ContainingNamespace.ToDisplayString();
- var className = typeSymbol.Name;
- var fullTypeName = typeSymbol.ToDisplayString();
- var isStruct = typeSymbol.IsValueType;
+// var className = typeSymbol.Name;
+// var fullTypeName = typeSymbol.ToDisplayString();
+// var isStruct = typeSymbol.IsValueType;
- // Check if this is a nested type
- var isNestedType = typeSymbol.ContainingType != null;
+// // 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;
+// // 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();
- 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)));
- }
- }
+// // Get all public properties with getter and setter
+// // DUPLICATED LOGIC: Same filtering as TypeMetadataBase.GetSerializableProperties()
+// var properties = new List();
+// 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));
+// // 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);
- }
+// return new SerializableClassInfo(namespaceName, className, fullTypeName, isStruct, isNestedType, typeNameForSignature, properties);
+// }
- ///
- /// Gets the nested type name chain (e.g., "OuterClass.MiddleClass.InnerClass")
- ///
- private static string GetNestedTypeName(INamedTypeSymbol typeSymbol)
- {
- var parts = new List();
- var current = typeSymbol;
+// ///
+// /// Gets the nested type name chain (e.g., "OuterClass.MiddleClass.InnerClass")
+// ///
+// private static string GetNestedTypeName(INamedTypeSymbol typeSymbol)
+// {
+// var parts = new List();
+// var current = typeSymbol;
- while (current != null)
- {
- parts.Insert(0, current.Name);
- current = current.ContainingType;
- }
+// while (current != null)
+// {
+// parts.Insert(0, current.Name);
+// current = current.ContainingType;
+// }
- return string.Join(".", parts);
- }
+// 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 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);
- }
+// 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);
- }
+// 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 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();
+// 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;
+// 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;
- }
+// return PropertyTypeKind.Unknown;
+// }
- private static bool IsCollectionType(ITypeSymbol type)
- {
- if (type is IArrayTypeSymbol)
- return true;
+// 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;
- }
- }
+// 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;
- }
+// return false;
+// }
- private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context)
- {
- if (classes.IsDefaultOrEmpty)
- return;
+// private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context)
+// {
+// if (classes.IsDefaultOrEmpty)
+// return;
- foreach (var classInfo in classes)
- {
- if (classInfo == null)
- continue;
+// 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));
- }
- }
+// 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();
+// private static string GenerateSerializerClass(SerializableClassInfo classInfo)
+// {
+// var sb = new StringBuilder();
- sb.AppendLine("// ");
- 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();
+// sb.AppendLine("// ");
+// 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("{");
- }
+// if (!string.IsNullOrEmpty(classInfo.Namespace))
+// {
+// sb.AppendLine($"namespace {classInfo.Namespace}");
+// sb.AppendLine("{");
+// }
- var indent = string.IsNullOrEmpty(classInfo.Namespace) ? "" : " ";
+// var indent = string.IsNullOrEmpty(classInfo.Namespace) ? "" : " ";
- sb.AppendLine($"{indent}/// ");
- sb.AppendLine($"{indent}/// Generated binary serializer for {classInfo.ClassName}.");
- sb.AppendLine($"{indent}/// ");
- sb.AppendLine($"{indent}internal static class {classInfo.ClassName}_AcBinarySerializer");
- sb.AppendLine($"{indent}{{");
+// sb.AppendLine($"{indent}/// ");
+// sb.AppendLine($"{indent}/// Generated binary serializer for {classInfo.ClassName}.");
+// sb.AppendLine($"{indent}/// ");
+// 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 count constant
+// sb.AppendLine($"{indent} public const int PropertyCount = {classInfo.Properties.Count};");
+// sb.AppendLine();
- // Generate property names array for validation
- sb.AppendLine($"{indent} /// ");
- sb.AppendLine($"{indent} /// Property names in serialization order (alphabetical).");
- sb.AppendLine($"{indent} /// Used for runtime validation against TypeMetadataBase.GetSerializableProperties().");
- sb.AppendLine($"{indent} /// ");
- sb.Append($"{indent} public static readonly string[] PropertyNames = new[] {{ ");
- sb.Append(string.Join(", ", classInfo.Properties.Select(p => $"\"{p.Name}\"")));
- sb.AppendLine(" };");
- sb.AppendLine();
+// // Generate property names array for validation
+// sb.AppendLine($"{indent} /// ");
+// sb.AppendLine($"{indent} /// Property names in serialization order (alphabetical).");
+// sb.AppendLine($"{indent} /// Used for runtime validation against TypeMetadataBase.GetSerializableProperties().");
+// sb.AppendLine($"{indent} /// ");
+// 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 Serialize method
+// GenerateSerializeMethod(sb, classInfo, indent);
- // Generate Deserialize method
- GenerateDeserializeMethod(sb, classInfo, indent);
+// // Generate Deserialize method
+// GenerateDeserializeMethod(sb, classInfo, indent);
- sb.AppendLine($"{indent}}}");
+// sb.AppendLine($"{indent}}}");
- if (!string.IsNullOrEmpty(classInfo.Namespace))
- {
- sb.AppendLine("}");
- }
+// if (!string.IsNullOrEmpty(classInfo.Namespace))
+// {
+// sb.AppendLine("}");
+// }
- return sb.ToString();
- }
+// return sb.ToString();
+// }
- private static void GenerateSerializeMethod(StringBuilder sb, SerializableClassInfo classInfo, string indent)
- {
- sb.AppendLine($"{indent} /// ");
- 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} /// ");
- sb.AppendLine($"{indent} [MethodImpl(MethodImplOptions.AggressiveInlining)]");
- sb.AppendLine($"{indent} public static void Serialize({classInfo.TypeNameForSignature} obj, ref AcBinarySerializer.BinarySerializationContext context)");
- sb.AppendLine($"{indent} {{");
+// private static void GenerateSerializeMethod(StringBuilder sb, SerializableClassInfo classInfo, string indent)
+// {
+// sb.AppendLine($"{indent} /// ");
+// 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} /// ");
+// sb.AppendLine($"{indent} [MethodImpl(MethodImplOptions.AggressiveInlining)]");
+// sb.AppendLine($"{indent} public static void Serialize({classInfo.TypeNameForSignature} obj, AcBinarySerializer.BinarySerializationContext context) where TOutput : BinaryOutputBase");
+// sb.AppendLine($"{indent} {{");
+// sb.AppendLine($"{indent} var output = context.Output;");
- foreach (var prop in classInfo.Properties)
- {
- GenerateSerializeProperty(sb, prop, indent + " ");
- }
+// foreach (var prop in classInfo.Properties)
+// {
+// GenerateSerializeProperty(sb, prop, indent + " ");
+// }
- sb.AppendLine($"{indent} }}");
- sb.AppendLine();
- }
+// sb.AppendLine($"{indent} }}");
+// sb.AppendLine();
+// }
- private static void GenerateSerializeProperty(StringBuilder sb, PropertyInfo prop, string indent)
- {
- var propAccess = $"obj.{prop.Name}";
+// private static void GenerateSerializeProperty(StringBuilder sb, PropertyInfo prop, string indent)
+// {
+// var propAccess = $"obj.{prop.Name}";
- // Handle nullable VALUE types (Nullable) - 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;
- }
+// // Handle nullable VALUE types (Nullable) - 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}");
+// 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;
- }
+// // 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;
- }
+// // 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);
- }
+// GenerateSerializeValue(sb, prop.TypeKind, propAccess, indent);
+// }
- ///
- /// Checks if the type kind represents a nullable VALUE type (Nullable<T>), not a reference type
- ///
- private static bool IsNullableValueTypeKind(PropertyTypeKind kind)
- {
- return kind >= PropertyTypeKind.NullableInt32 && kind <= PropertyTypeKind.NullableEnum;
- }
+// ///
+// /// Checks if the type kind represents a nullable VALUE type (Nullable<T>), not a reference type
+// ///
+// 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;
+// 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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;
+// 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;
- }
- }
+// 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} /// ");
- 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} /// ");
- sb.AppendLine($"{indent} [MethodImpl(MethodImplOptions.AggressiveInlining)]");
- sb.AppendLine($"{indent} public static void Deserialize({classInfo.TypeNameForSignature} obj, ref AcBinaryDeserializer.BinaryDeserializationContext context)");
- sb.AppendLine($"{indent} {{");
+// private static void GenerateDeserializeMethod(StringBuilder sb, SerializableClassInfo classInfo, string indent)
+// {
+// sb.AppendLine($"{indent} /// ");
+// 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} /// ");
+// 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 + " ");
- }
+// foreach (var prop in classInfo.Properties)
+// {
+// GenerateDeserializeProperty(sb, prop, indent + " ");
+// }
- sb.AppendLine($"{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();");
+// 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 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 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} }}");
+// // Handle actual value
+// sb.AppendLine($"{indent} else");
+// sb.AppendLine($"{indent} {{");
+// GenerateDeserializeValue(sb, prop, indent + " ");
+// sb.AppendLine($"{indent} }}");
- sb.AppendLine($"{indent}}}");
- }
+// sb.AppendLine($"{indent}}}");
+// }
- private static void GenerateDeserializeValue(StringBuilder sb, PropertyInfo prop, string indent)
- {
- var propAccess = $"obj.{prop.Name}";
+// 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;
+// 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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;
+// 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;
- }
- }
-}
+// default:
+// sb.AppendLine($"{indent}throw new NotImplementedException($\"Type deserialization not implemented\");");
+// break;
+// }
+// }
+//}
-///
-/// Information about a class marked with [AcBinarySerializable].
-///
-internal sealed class SerializableClassInfo
-{
- public string Namespace { get; }
- public string ClassName { get; }
- public string FullTypeName { get; }
- public bool IsStruct { get; }
- public bool IsNestedType { get; }
- ///
- /// The type name to use in method signatures. For nested types this includes the containing types.
- /// e.g., "OuterClass.InnerClass"
- ///
- public string TypeNameForSignature { get; }
- public List Properties { get; }
+/////
+///// Information about a class marked with [AcBinarySerializable].
+/////
+//internal sealed class SerializableClassInfo
+//{
+// public string Namespace { get; }
+// public string ClassName { get; }
+// public string FullTypeName { get; }
+// public bool IsStruct { get; }
+// public bool IsNestedType { get; }
+// ///
+// /// The type name to use in method signatures. For nested types this includes the containing types.
+// /// e.g., "OuterClass.InnerClass"
+// ///
+// public string TypeNameForSignature { get; }
+// public List Properties { get; }
- public SerializableClassInfo(string ns, string className, string fullTypeName, bool isStruct, bool isNestedType, string typeNameForSignature, List properties)
- {
- Namespace = ns;
- ClassName = className;
- FullTypeName = fullTypeName;
- IsStruct = isStruct;
- IsNestedType = isNestedType;
- TypeNameForSignature = typeNameForSignature;
- Properties = properties;
- }
-}
+// public SerializableClassInfo(string ns, string className, string fullTypeName, bool isStruct, bool isNestedType, string typeNameForSignature, List properties)
+// {
+// Namespace = ns;
+// ClassName = className;
+// FullTypeName = fullTypeName;
+// IsStruct = isStruct;
+// IsNestedType = isNestedType;
+// TypeNameForSignature = typeNameForSignature;
+// Properties = properties;
+// }
+//}
-///
-/// Information about a serializable property.
-///
-internal sealed class PropertyInfo
-{
- public string Name { get; }
- public string TypeName { get; }
- public PropertyTypeKind TypeKind { get; }
- public bool IsNullable { get; }
+/////
+///// Information about a serializable property.
+/////
+//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;
- }
-}
+// public PropertyInfo(string name, string typeName, PropertyTypeKind typeKind, bool isNullable)
+// {
+// Name = name;
+// TypeName = typeName;
+// TypeKind = typeKind;
+// IsNullable = isNullable;
+// }
+//}
-///
-/// Kind of property type for code generation.
-///
-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
-}
+/////
+///// Kind of property type for code generation.
+/////
+//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
+//}
diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
index 746c28c..92b524c 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializeTypeMetadata.cs
@@ -41,7 +41,7 @@ public static partial class AcBinaryDeserializer
}
// Fast check: only look for generated serializer if type has [AcBinarySerializable] attribute
- if (type.IsDefined(typeof(AcBinarySerializableAttribute), inherit: false))
+ if (false && type.IsDefined(typeof(AcBinarySerializableAttribute), inherit: false))
{
GeneratedSerializerType = FindGeneratedSerializerType(type);
HasGeneratedDeserializer = GeneratedSerializerType != null;
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
index 400ad83..3a5cccd 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
@@ -2,11 +2,7 @@ using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Numerics;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Text;
using System.Threading;
using static AyCode.Core.Helpers.JsonUtilities;
@@ -14,12 +10,12 @@ namespace AyCode.Core.Serializers.Binaries;
public static partial class AcBinarySerializer
{
- private static class BinarySerializationContextPool
+ private static class BinarySerializationContextPool where TOutput : BinaryOutputBase
{
- private static readonly ConcurrentQueue Pool = new();
+ private static readonly ConcurrentQueue> Pool = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static BinarySerializationContext Get(AcBinarySerializerOptions options)
+ public static BinarySerializationContext Get(AcBinarySerializerOptions options)
{
if (Pool.TryDequeue(out var context))
{
@@ -27,17 +23,17 @@ public static partial class AcBinarySerializer
return context;
}
- return new BinarySerializationContext(options);
+ return new BinarySerializationContext(options);
}
- public static void ReturnAsync(BinarySerializationContext context)
+ public static void ReturnAsync(BinarySerializationContext context)
{
// 🔥 FIRE-AND-FORGET: cleanup háttérben
ThreadPool.UnsafeQueueUserWorkItem(Return, context, preferLocal: true);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Return(BinarySerializationContext context)
+ public static void Return(BinarySerializationContext context)
{
if (Pool.Count < context.Options.MaxContextPoolSize)
{
@@ -51,50 +47,23 @@ public static partial class AcBinarySerializer
}
}
- public static int GrowBufferCount =>
-#if DEBUG
- BinarySerializationContext.GrowBufferCount;
-#else
- -1;
-#endif
-
- public static long GrowBufferTotalBytes =>
-#if DEBUG
- BinarySerializationContext.GrowBufferTotalBytes;
-#else
- -1;
-#endif
-
///
- /// Binary serialization context. Public for generated serializers.
+ /// Binary serialization context. Generic on TOutput for JIT devirtualization.
+ /// TOutput is the binary output target (ArrayBinaryOutput for byte[], BufferWriterBinaryOutput for IBufferWriter).
+ /// All write operations are delegated to Output — the context only manages serialization state
+ /// (string interning, reference tracking, metadata, property filtering).
///
- internal sealed class BinarySerializationContext : SerializationContextBase, IDisposable
+ internal sealed class BinarySerializationContext
+ : SerializationContextBase, IDisposable
+ where TOutput : BinaryOutputBase
{
- private const int MinBufferSize = 512;
- private const int BufferHalvingThreshold = 4; // Halve buffer when > _initialBufferSize * this
private const int PropertyIndexBufferMaxCache = 512;
private const int PropertyStateBufferMaxCache = 512;
- private const int InitialInternCapacity = 32;
- private byte[] _buffer;
- private int _position;
- private int _initialBufferSize;
-
-#if DEBUG
- ///
- /// Counts how many times GrowBuffer was called during serialization.
- /// Used for benchmarking buffer allocation efficiency.
- ///
- public static int GrowBufferCount { get; set; }
///
- /// Total bytes allocated by GrowBuffer during serialization.
- /// Used for benchmarking buffer allocation efficiency.
+ /// Strongly-typed output target for JIT devirtualization in the write pass.
///
- public static long GrowBufferTotalBytes { get; set; }
-#endif
-
- // Use shared reference tracker from AcSerializerCommon
- //private readonly AcSerializerCommon.SerializationReferenceTracker _refTracker = new();
+ public TOutput Output = default!;
private IdentityMap? _stringInternMap;
private int _nextCacheIndex; // Next dense cache index to assign (starts at 0, uses ++_nextCacheIndex)
@@ -160,12 +129,17 @@ public static partial class AcBinarySerializer
///
public bool HasPropertyFilter { get; private set; }
- public int Position => _position;
+ ///
+ /// Current output position (delegates to Output).
+ ///
+ public int Position
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => Output.Position;
+ }
public BinarySerializationContext(AcBinarySerializerOptions options)
{
- _initialBufferSize = Math.Max(options.InitialBufferCapacity, MinBufferSize);
- _buffer = ArrayPool.Shared.Rent(_initialBufferSize);
Reset(options);
}
@@ -179,26 +153,11 @@ public static partial class AcBinarySerializer
{
// IMPORTANT: base.Reset sets Options first, so derived code can use Options-derived properties
base.Reset(options);
-
- _position = 0;
- _initialBufferSize = Math.Max(Options.InitialBufferCapacity, MinBufferSize);
HasPropertyFilter = Options.PropertyFilter != null;
-
- // NOTE: GrowBufferCount és GrowBufferTotalBytes NEM nullázódik itt!
- // Kumulatívan gyűjtjük a benchmark során.
-
- if (_buffer.Length < _initialBufferSize || _buffer.Length > _initialBufferSize * BufferHalvingThreshold)
- {
- ArrayPool.Shared.Return(_buffer);
- _buffer = ArrayPool.Shared.Rent(_initialBufferSize);
- }
}
public override void Clear()
{
- _position = 0;
-
- //_refTracker.Reset();
_stringInternMap?.Reset();
_nextCacheIndex = 0;
_nextFirstIndex = 0;
@@ -215,27 +174,12 @@ public static partial class AcBinarySerializer
_propertyStateBuffer = null;
}
- // Halve oversized output buffer (IdentityMap pattern: gradual shrink after spike)
- if (_buffer.Length > _initialBufferSize * BufferHalvingThreshold)
- {
- var nextSize = Math.Max(_buffer.Length / 2, _initialBufferSize);
- ArrayPool.Shared.Return(_buffer);
- _buffer = ArrayPool.Shared.Rent(nextSize);
- }
-
// Clear wrapper tracking - returns IdentityMap arrays to pool
base.Clear();
}
-
public void Dispose()
{
- if (_buffer != null)
- {
- ArrayPool.Shared.Return(_buffer);
- _buffer = null!;
- }
-
if (_propertyIndexBuffer != null)
{
ArrayPool.Shared.Return(_propertyIndexBuffer);
@@ -248,6 +192,9 @@ public static partial class AcBinarySerializer
_propertyStateBuffer = null;
}
+ // Dispose the output if it implements IDisposable (e.g. ArrayBinaryOutput returns buffer to pool)
+ if (Output is IDisposable disposableOutput)
+ disposableOutput.Dispose();
}
#region String Interning
@@ -262,7 +209,7 @@ public static partial class AcBinarySerializer
if (_stringInternMap == null)
{
found = false;
- return ref System.Runtime.CompilerServices.Unsafe.NullRef();
+ return ref Unsafe.NullRef();
}
if (_stringInternMap.TryAdd(value, out var slotIndex))
@@ -312,7 +259,6 @@ public static partial class AcBinarySerializer
///
public int GetCacheCount() => _nextCacheIndex;
-
#endregion
#region UseMetadata Type Tracking
@@ -338,17 +284,17 @@ public static partial class AcBinarySerializer
/// Ismételt: [propNameHash (4b)]
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteInlineMetadata(BinarySerializeTypeMetadata metadata, bool isFirstOccurrence)
+ public void WriteInlineMetadata(BinarySerializeTypeMetadata metadata, TOutput output, bool isFirstOccurrence)
{
- WriteRaw(metadata.PropNameHash);
+ output.WriteRaw(metadata.PropNameHash);
if (isFirstOccurrence)
{
var hashes = metadata.MetadataPropertyHashes;
- WriteVarUInt((uint)hashes.Length);
+ output.WriteVarUInt((uint)hashes.Length);
for (var i = 0; i < hashes.Length; i++)
{
- WriteRaw(hashes[i]);
+ output.WriteRaw(hashes[i]);
}
}
}
@@ -382,42 +328,6 @@ public static partial class AcBinarySerializer
#endregion
- #region Output
-
- ///
- /// Returns the serialized data as a ReadOnlySpan without allocation.
- /// Use this for compression or other processing before final ToArray().
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan AsSpan() => _buffer.AsSpan(0, _position);
-
- public byte[] ToArray()
- {
- var result = GC.AllocateUninitializedArray(_position);
- _buffer.AsSpan(0, _position).CopyTo(result);
- return result;
- }
-
- public void WriteTo(IBufferWriter writer)
- {
- var span = writer.GetSpan(_position);
- _buffer.AsSpan(0, _position).CopyTo(span);
- writer.Advance(_position);
- }
-
- public BinarySerializationResult DetachResult()
- {
- var resultBuffer = _buffer;
- var resultLength = _position;
-
- _buffer = ArrayPool.Shared.Rent(_initialBufferSize);
- _position = 0;
-
- return new BinarySerializationResult(resultBuffer, resultLength, pooled: true);
- }
-
- #endregion
-
#region Property Filtering
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -441,440 +351,8 @@ public static partial class AcBinarySerializer
#endregion
- #region Buffer Helpers
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void EnsureCapacity(int additionalBytes)
- {
- var required = _position + additionalBytes;
- if (required <= _buffer.Length)
- {
- return;
- }
-
- GrowBuffer(required);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void GrowBuffer(int required)
- {
- var newSize = Math.Max(_buffer.Length * 2, required);
- var newBuffer = ArrayPool.Shared.Rent(newSize);
- _buffer.AsSpan(0, _position).CopyTo(newBuffer);
- ArrayPool.Shared.Return(_buffer);
- _buffer = newBuffer;
-
- #if DEBUG
- GrowBufferCount++;
- GrowBufferTotalBytes += newSize;
- #endif
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteByte(byte value)
- {
- if (_position >= _buffer.Length)
- {
- GrowBuffer(_position + 1);
- }
- _buffer[_position++] = value;
- }
-
- ///
- /// Write type code byte followed by a raw value. Batches EnsureCapacity call.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteTypeCodeAndRaw(byte typeCode, T value) where T : unmanaged
- {
- var size = 1 + Unsafe.SizeOf();
- EnsureCapacity(size);
- _buffer[_position++] = typeCode;
- Unsafe.WriteUnaligned(ref _buffer[_position], value);
- _position += Unsafe.SizeOf();
- }
-
- ///
- /// Write two bytes efficiently.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteTwoBytes(byte b1, byte b2)
- {
- EnsureCapacity(2);
- _buffer[_position++] = b1;
- _buffer[_position++] = b2;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteBytes(ReadOnlySpan data)
- {
- EnsureCapacity(data.Length);
- data.CopyTo(_buffer.AsSpan(_position));
- _position += data.Length;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteRaw(T value) where T : unmanaged
- {
- var size = Unsafe.SizeOf();
- EnsureCapacity(size);
- Unsafe.WriteUnaligned(ref _buffer[_position], value);
- _position += size;
- }
-
- #endregion
-
- #region Specialized Writers
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteDecimalBits(decimal value)
- {
- EnsureCapacity(16);
- Span bits = stackalloc int[4];
- decimal.TryGetBits(value, bits, out _);
- MemoryMarshal.AsBytes(bits).CopyTo(_buffer.AsSpan(_position, 16));
- _position += 16;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteDateTimeBits(DateTime value)
- {
- EnsureCapacity(9);
- Unsafe.WriteUnaligned(ref _buffer[_position], value.Ticks);
- _buffer[_position + 8] = (byte)value.Kind;
- _position += 9;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteGuidBits(Guid value)
- {
- EnsureCapacity(16);
- value.TryWriteBytes(_buffer.AsSpan(_position, 16));
- _position += 16;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteDateTimeOffsetBits(DateTimeOffset value)
- {
- EnsureCapacity(10);
- Unsafe.WriteUnaligned(ref _buffer[_position], value.UtcTicks);
- Unsafe.WriteUnaligned(ref _buffer[_position + 8], (short)value.Offset.TotalMinutes);
- _position += 10;
- }
-
-
- public void WriteVarInt(int value)
- {
- var encoded = (uint)((value << 1) ^ (value >> 31));
- // Fast path for small positive values (0-63 when ZigZag encoded)
- if (encoded < 0x80)
- {
- EnsureCapacity(1);
- _buffer[_position++] = (byte)encoded;
- return;
- }
- EnsureCapacity(5);
- WriteVarUIntInternal(encoded);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVarUInt(uint value)
- {
- // Fast path for small values (0-127)
- if (value < 0x80)
- {
- EnsureCapacity(1);
- _buffer[_position++] = (byte)value;
- return;
- }
- EnsureCapacity(5);
- WriteVarUIntInternal(value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteVarUIntInternal(uint value)
- {
- while (value >= 0x80)
- {
- _buffer[_position++] = (byte)(value | 0x80);
- value >>= 7;
- }
-
- _buffer[_position++] = (byte)value;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVarLong(long value)
- {
- var encoded = (ulong)((value << 1) ^ (value >> 63));
- // Fast path for small values
- if (encoded < 0x80)
- {
- EnsureCapacity(1);
- _buffer[_position++] = (byte)encoded;
- return;
- }
- EnsureCapacity(10);
- WriteVarULongInternal(encoded);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteVarULong(ulong value)
- {
- // Fast path for small values (0-127)
- if (value < 0x80)
- {
- EnsureCapacity(1);
- _buffer[_position++] = (byte)value;
- return;
- }
- EnsureCapacity(10);
- WriteVarULongInternal(value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteVarULongInternal(ulong value)
- {
- while (value >= 0x80)
- {
- _buffer[_position++] = (byte)(value | 0x80);
- value >>= 7;
- }
-
- _buffer[_position++] = (byte)value;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteStringUtf8(string value)
- {
- // Fast path for ASCII-only strings using SIMD-optimized check
- if (Ascii.IsValid(value))
- {
- WriteVarUInt((uint)value.Length);
- EnsureCapacity(value.Length);
- // Use System.Text.Ascii for SIMD-optimized ASCII to bytes conversion
- Ascii.FromUtf16(value.AsSpan(), _buffer.AsSpan(_position, value.Length), out _);
- _position += value.Length;
- return;
- }
-
- // Standard path for multi-byte UTF8
- var byteCount = Utf8NoBom.GetByteCount(value);
- WriteVarUInt((uint)byteCount);
- EnsureCapacity(byteCount);
- Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
- _position += byteCount;
- }
-
- ///
- /// Checks if string contains only ASCII characters (0-127).
- /// Optimized loop with early exit.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsAscii(string value)
- {
- var span = value.AsSpan();
- for (var i = 0; i < span.Length; i++)
- {
- if (span[i] > 127)
- return false;
- }
- return true;
- }
-
- ///
- /// Writes ASCII string directly to byte buffer (char to byte, no encoding needed).
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteAsciiDirect(ReadOnlySpan source, Span destination)
- {
- for (var i = 0; i < source.Length; i++)
- {
- destination[i] = (byte)source[i];
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WritePreencodedPropertyName(ReadOnlySpan utf8Name)
- {
- WriteByte(BinaryTypeCode.String);
- WriteVarUInt((uint)utf8Name.Length);
- WriteBytes(utf8Name);
- }
-
- public void WriteInt32ArrayOptimized(int[] array)
- {
- for (var i = 0; i < array.Length; i++)
- {
- var value = array[i];
- if (BinaryTypeCode.TryEncodeTinyInt(value, out var tiny))
- {
- WriteByte(tiny);
- }
- else
- {
- WriteByte(BinaryTypeCode.Int32);
- WriteVarInt(value);
- }
- }
- }
-
- public void WriteLongArrayOptimized(long[] array)
- {
- for (var i = 0; i < array.Length; i++)
- {
- var value = array[i];
- if (value >= int.MinValue && value <= int.MaxValue)
- {
- var intValue = (int)value;
- if (BinaryTypeCode.TryEncodeTinyInt(intValue, out var tiny))
- {
- WriteByte(tiny);
- }
- else
- {
- WriteByte(BinaryTypeCode.Int32);
- WriteVarInt(intValue);
- }
- }
- else
- {
- WriteByte(BinaryTypeCode.Int64);
- WriteVarLong(value);
- }
- }
- }
-
- public void WriteDoubleArrayBulk(double[] array)
- {
- EnsureCapacity(array.Length * 9);
- for (var i = 0; i < array.Length; i++)
- {
- _buffer[_position++] = BinaryTypeCode.Float64;
- Unsafe.WriteUnaligned(ref _buffer[_position], array[i]);
- _position += 8;
- }
- }
-
- public void WriteFloatArrayBulk(float[] array)
- {
- EnsureCapacity(array.Length * 5);
- for (var i = 0; i < array.Length; i++)
- {
- _buffer[_position++] = BinaryTypeCode.Float32;
- Unsafe.WriteUnaligned(ref _buffer[_position], array[i]);
- _position += 4;
- }
- }
-
- public void WriteGuidArrayBulk(Guid[] array)
- {
- EnsureCapacity(array.Length * 17);
- for (var i = 0; i < array.Length; i++)
- {
- _buffer[_position++] = BinaryTypeCode.Guid;
- array[i].TryWriteBytes(_buffer.AsSpan(_position, 16));
- _position += 16;
- }
- }
-
- #endregion
-
- #region SIMD Bulk Copy
-
- ///
- /// Copy bytes using SIMD when available, otherwise fall back to standard copy.
- /// Optimized for Blazor WASM where Vector operations are supported.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteBytesSimd(ReadOnlySpan source)
- {
- EnsureCapacity(source.Length);
- var destination = _buffer.AsSpan(_position, source.Length);
-
- if (Vector.IsHardwareAccelerated && source.Length >= Vector.Count * 2)
- {
- CopyWithSimd(source, destination);
- }
- else
- {
- source.CopyTo(destination);
- }
-
- _position += source.Length;
- }
-
- ///
- /// SIMD-optimized memory copy for large buffers.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void CopyWithSimd(ReadOnlySpan source, Span destination)
- {
- var vectorSize = Vector.Count;
- var i = 0;
- var length = source.Length;
-
- // Process full vectors
- var vectorCount = length / vectorSize;
- for (var v = 0; v < vectorCount; v++)
- {
- var vec = new Vector(source.Slice(i, vectorSize));
- vec.CopyTo(destination.Slice(i, vectorSize));
- i += vectorSize;
- }
-
- // Copy remaining bytes
- if (i < length)
- {
- source.Slice(i).CopyTo(destination.Slice(i));
- }
- }
-
- ///
- /// Write double array using SIMD bulk copy (no per-element type codes).
- /// For use when caller handles type codes separately.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteDoubleBulkRaw(ReadOnlySpan values)
- {
- var byteSpan = MemoryMarshal.AsBytes(values);
- WriteBytesSimd(byteSpan);
- }
-
- ///
- /// Write float array using SIMD bulk copy.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteFloatBulkRaw(ReadOnlySpan values)
- {
- var byteSpan = MemoryMarshal.AsBytes(values);
- WriteBytesSimd(byteSpan);
- }
-
- ///
- /// Write Guid array using SIMD bulk copy.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteGuidBulkRaw(ReadOnlySpan values)
- {
- // Guid is 16 bytes, perfect for SIMD
- var byteLength = values.Length * 16;
- EnsureCapacity(byteLength);
-
- for (var i = 0; i < values.Length; i++)
- {
- values[i].TryWriteBytes(_buffer.AsSpan(_position, 16));
- _position += 16;
- }
- }
-
- #endregion
-
#region Header
- // Marker-based interning: no footer needed
- // Header: [version][flags][cacheCount (VarUInt, if caching enabled)]
- // Body: data with markers (StringInternFirst, ObjectRefFirst, etc.)
-
///
/// Writes the binary header directly. Call AFTER ScanForDuplicates (cacheCount is known).
/// No placeholder, no shift — single forward write.
@@ -892,48 +370,17 @@ public static partial class AcBinarySerializer
if (HasCaching)
flags |= BinaryTypeCode.HeaderFlag_HasCacheCount;
- WriteByte(AcBinarySerializerOptions.FormatVersion);
- WriteByte(flags);
+ Output.WriteByte(AcBinarySerializerOptions.FormatVersion);
+ Output.WriteByte(flags);
if (HasCaching)
{
- WriteVarUInt((uint)GetCacheCount());
+ Output.WriteVarUInt((uint)GetCacheCount());
}
}
#endregion
- #region Reference Handling
-
- //[MethodImpl(MethodImplOptions.AggressiveInlining)]
- //public bool TrackForScanning(object obj) => _refTracker.TrackForScanning(obj);
-
- ///
- /// IId-aware tracking for the scan phase.
- /// First checks IId match (different instance, same Id), then falls back to ReferenceEquals.
- ///
- //[MethodImpl(MethodImplOptions.AggressiveInlining)]
- //public bool TrackForScanningWithIId(object obj, BinarySerializeTypeMetadata metadata, out int existingRefId)
- //{
- // if (!ReferenceHandling)
- // {
- // existingRefId = 0;
- // return true; // No tracking needed
- // }
- // return _refTracker.TrackForScanningWithIId(obj, metadata, out existingRefId);
- //}
-
- //[MethodImpl(MethodImplOptions.AggressiveInlining)]
- //public bool ShouldWriteRef(object obj, out int refId) => _refTracker.ShouldWriteId(obj, out refId);
-
- //[MethodImpl(MethodImplOptions.AggressiveInlining)]
- //public void MarkAsWritten(object obj, int refId) => _refTracker.MarkAsWritten(obj, refId);
-
- //[MethodImpl(MethodImplOptions.AggressiveInlining)]
- //public bool TryGetExistingRef(object obj, out int refId) => _refTracker.TryGetExistingRef(obj, out refId);
-
- #endregion
-
#region Helpers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -968,79 +415,5 @@ public static partial class AcBinarySerializer
}
#endregion
-
- #region FixStr Methods
-
- ///
- /// Write short ASCII string using FixStr encoding (type+length in single byte).
- /// Only call when string is ASCII and length <= 31.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteFixStr(string value)
- {
- var length = value.Length;
- EnsureCapacity(1 + length);
- _buffer[_position++] = BinaryTypeCode.EncodeFixStr(length);
- Ascii.FromUtf16(value.AsSpan(), _buffer.AsSpan(_position, length), out _);
- _position += length;
- }
-
- ///
- /// Optimized FixStr write: tries SIMD ASCII conversion, falls back to UTF8.
- /// Single-pass: uses Ascii.FromUtf16 which does validation + copy.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteFixStrDirect(string value)
- {
- var length = value.Length;
- EnsureCapacity(1 + length);
-
- // Ascii.FromUtf16: SIMD-optimized ASCII conversion
- // Returns actual bytes written - if less than input length, there was a non-ASCII char
- var destSpan = _buffer.AsSpan(_position + 1, length);
- var status = Ascii.FromUtf16(value.AsSpan(), destSpan, out var bytesWritten);
-
- if (status == System.Buffers.OperationStatus.Done && bytesWritten == length)
- {
- // Success - write FixStr header
- _buffer[_position] = BinaryTypeCode.EncodeFixStr(length);
- _position += 1 + length;
- }
- else
- {
- // Non-ASCII or partial - use standard string encoding
- _buffer[_position++] = BinaryTypeCode.String;
- WriteStringUtf8Internal(value);
- }
- }
-
- ///
- /// Internal string write (after String type code already written).
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteStringUtf8Internal(string value)
- {
- var byteCount = Utf8NoBom.GetByteCount(value);
- WriteVarUInt((uint)byteCount);
- EnsureCapacity(byteCount);
- Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
- _position += byteCount;
- }
-
- ///
- /// Write short UTF8 bytes using FixStr encoding.
- /// Only call when byteLength <= 31.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteFixStrBytes(ReadOnlySpan utf8Bytes)
- {
- var length = utf8Bytes.Length;
- EnsureCapacity(1 + length);
- _buffer[_position++] = BinaryTypeCode.EncodeFixStr(length);
- utf8Bytes.CopyTo(_buffer.AsSpan(_position, length));
- _position += length;
- }
-
- #endregion
}
}
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializeTypeMetadata.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializeTypeMetadata.cs
index 7cf0410..ab31d3b 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializeTypeMetadata.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializeTypeMetadata.cs
@@ -144,7 +144,7 @@ public static partial class AcBinarySerializer
NeedsReferenceTracking = IsIId || HasComplexProperties || !IsPrimitiveType;
// Fast check: only look for generated serializer if type has [AcBinarySerializable] attribute
- if (type.IsDefined(typeof(AcBinarySerializableAttribute), inherit: false))
+ if (false && type.IsDefined(typeof(AcBinarySerializableAttribute), inherit: false))
{
GeneratedSerializerType = FindGeneratedSerializerType(type);
HasGeneratedSerializer = GeneratedSerializerType != null;
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.ScanPass.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.ScanPass.cs
index ab36030..c2ff1ab 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.ScanPass.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.ScanPass.cs
@@ -15,7 +15,8 @@ public static partial class AcBinarySerializer
/// - Strings/objects skipped here are never written anyway (parent is ObjectRef)
/// CacheIndex is assigned immediately on 2nd occurrence (no post-processing needed).
///
- private static void ScanForDuplicates(object value, Type type, BinarySerializationContext context)
+ private static void ScanForDuplicates(object value, Type type, BinarySerializationContext context)
+ where TOutput : BinaryOutputBase
{
if (!context.HasCaching)
return;
@@ -24,7 +25,8 @@ public static partial class AcBinarySerializer
ScanValue(value, wrapper, context, 0);
}
- private static void ScanValue(object? value, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth)
+ private static void ScanValue(object? value, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
if (value == null || depth > context.MaxDepth)
return;
@@ -128,7 +130,8 @@ public static partial class AcBinarySerializer
/// Scans a collection item. Handles string fast path and gets wrapper for the runtime type.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void ScanItem(object item, BinarySerializationContext context, int depth)
+ private static void ScanItem(object item, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
// String fast path — avoid GetWrapper entirely
if (item is string str)
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
index aa4964e..8b5c485 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
@@ -25,6 +25,7 @@ namespace AyCode.Core.Serializers.Binaries;
/// - Optimized buffer management with ArrayPool
/// - Zero-allocation hot paths using Span and MemoryMarshal
/// - Automatic Expression to AcExpressionNode conversion
+/// - Generic TOutput for JIT devirtualization (ArrayBinaryOutput / BufferWriterBinaryOutput)
///
public static partial class AcBinarySerializer
{
@@ -57,9 +58,9 @@ public static partial class AcBinarySerializer
public static Dictionary> AnalyzeStringInternCandidates(T value, AcBinarySerializerOptions? options = null)
{
ArgumentNullException.ThrowIfNull(value);
-
+
options ??= AcBinarySerializerOptions.Default;
-
+
// For analysis, use the provided reference handling mode
var analysisOptions = new AcBinarySerializerOptions
{
@@ -68,13 +69,14 @@ public static partial class AcBinarySerializer
MaxStringInternLength = options.MaxStringInternLength,
ReferenceHandling = options.ReferenceHandling
};
-
+
var result = new Dictionary>();
var runtimeType = value.GetType();
-
+
// Create context without pooling (we need to set up callback)
- using var context = new BinarySerializationContext(analysisOptions);
-
+ using var context = new BinarySerializationContext(analysisOptions);
+ context.Output = new ArrayBinaryOutput();
+
// Set up tracking callbacks
context.OnStringInterned = (propertyPath, stringValue) =>
{
@@ -88,11 +90,11 @@ public static partial class AcBinarySerializer
properties[propertyPath] = properties.GetValueOrDefault(propertyPath) + 1;
};
-
+
// Run serialization to trigger callbacks
context.WriteHeader();
WriteValue(value, runtimeType, context, 0);
-
+
return result;
}
@@ -104,7 +106,7 @@ public static partial class AcBinarySerializer
// Transform: stringValue → properties TO propertyPath → (stringValue, count)
var propertyStats = new Dictionary>();
-
+
foreach (var (stringValue, properties) in analysis)
{
var byteLength = Encoding.UTF8.GetByteCount(stringValue);
@@ -131,7 +133,7 @@ public static partial class AcBinarySerializer
var totalStrings = analysis.Values.Sum(p => p.Values.Sum());
var uniqueStrings = analysis.Count;
var repeatedStrings = analysis.Count(kv => kv.Value.Values.Sum() > 1);
-
+
sb.AppendLine("+-----------------------------------------------------------------------------+");
sb.AppendLine("| STRING SUMMARY |");
sb.AppendLine("+-----------------------------------------------------------------------------+");
@@ -150,13 +152,13 @@ public static partial class AcBinarySerializer
var repeated = strings.Count(s => s.Count > 1);
var repeatSum = strings.Where(s => s.Count > 1).Sum(s => s.Count); // Sum of occurrences of repeated strings
var repeatSumPct = total > 0 ? repeatSum * 100.0 / total : 0;
-
+
// Calculate savings
var totalBytes = strings.Sum(s => s.Count * s.ByteLength);
var uniqueBytes = strings.Sum(s => s.ByteLength);
var indexBytes = total * 2;
var savings = totalBytes - (uniqueBytes + indexBytes);
-
+
return (propPath, strings, total, unique, repeated, repeatSum, repeatSumPct, savings);
}).ToList();
@@ -168,7 +170,7 @@ public static partial class AcBinarySerializer
{
var savingsStr = stat.savings > 0 ? $"+{stat.savings:N0}B" : $"{stat.savings:N0}B";
var recommend = stat.savings > 100 ? "[INTERN]" : stat.savings > 0 ? " maybe " : " skip ";
-
+
var propDisplay = stat.propPath.Length > 30 ? stat.propPath[..27] + "..." : stat.propPath;
sb.AppendLine($"| {propDisplay,-30} | {stat.total,5} | {stat.repeatSum,7} | {stat.unique,6} | {stat.repeated,7} | {stat.repeatSumPct,8:F1}% | {savingsStr,8} | {recommend,-11} |");
}
@@ -187,14 +189,14 @@ public static partial class AcBinarySerializer
{
sb.AppendLine();
sb.AppendLine($" {stat.propPath} (RepSum: {stat.repeatSum}/{stat.total} = {stat.repeatSumPct:F1}%):");
-
+
foreach (var (strVal, count, _) in stat.strings.OrderByDescending(s => s.Count).Take(10))
{
var preview = strVal.Length > 40 ? strVal[..37] + "..." : strVal;
var marker = count > 1 ? ">" : " ";
sb.AppendLine($" {marker} [{count,4}x] \"{preview}\"");
}
-
+
if (stat.strings.Count > 10)
{
sb.AppendLine($" ... and {stat.strings.Count - 10} more unique values");
@@ -208,7 +210,7 @@ public static partial class AcBinarySerializer
sb.AppendLine(" RepSum%=percentage of occurrences that are repeated (higher=better for intern)");
sb.AppendLine(" Savings=estimated bytes saved with interning (positive=good)");
sb.AppendLine(" > = repeated string (benefits from interning)");
-
+
return sb;
}
#endif
@@ -227,6 +229,7 @@ public static partial class AcBinarySerializer
///
/// Serialize object to binary with specified options.
+ /// Uses ArrayBinaryOutput for byte[] result path.
///
public static byte[] Serialize(T value, AcBinarySerializerOptions options)
{
@@ -236,7 +239,7 @@ public static partial class AcBinarySerializer
}
var runtimeType = value.GetType();
-
+
// Handle IQueryable types - convert to AcExpressionNode (serialize the Expression)
object actualValue = value;
if (value is IQueryable queryable)
@@ -250,29 +253,38 @@ public static partial class AcBinarySerializer
actualValue = AcExpressionConverter.ToNode((Expression)(object)value);
runtimeType = typeof(AcExpressionNode);
}
-
- var context = SerializeCore(actualValue, runtimeType, options);
+
+ var context = BinarySerializationContextPool.Get(options);
+ if (context.Output == null)
+ context.Output = new ArrayBinaryOutput(options.InitialBufferCapacity);
+ else
+ context.Output.Reset();
+
try
{
+ ScanForDuplicates(actualValue, runtimeType, context);
+ context.WriteHeader();
+ WriteValue(actualValue, runtimeType, context, 0);
+
// Apply compression if enabled - compress directly from buffer span (1 allocation)
if (options.UseCompression != Lz4CompressionMode.None)
{
- return Lz4.Compress(context.AsSpan(), options.UseCompression);
+ return Lz4.Compress(context.Output.AsSpan(), options.UseCompression);
}
-
+
// No compression - single allocation for result
- return context.ToArray();
+ return context.Output.ToArray();
}
finally
{
- if (context.Options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
- else BinarySerializationContextPool.Return(context);
+ if (options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
+ else BinarySerializationContextPool.Return(context);
}
}
///
/// Serialize object to an IBufferWriter for zero-copy scenarios.
- /// This avoids the final ToArray() allocation by writing directly to the caller's buffer.
+ /// Uses BufferWriterBinaryOutput — writes directly to the caller's buffer.
/// Note: Compression is applied if enabled in options.
///
public static void Serialize(T value, IBufferWriter writer, AcBinarySerializerOptions options)
@@ -286,7 +298,7 @@ public static partial class AcBinarySerializer
}
var runtimeType = value.GetType();
-
+
// Handle IQueryable types - convert to AcExpressionNode (serialize the Expression)
object actualValue = value;
if (value is IQueryable queryable)
@@ -300,27 +312,37 @@ public static partial class AcBinarySerializer
actualValue = AcExpressionConverter.ToNode((Expression)(object)value);
runtimeType = typeof(AcExpressionNode);
}
-
- var context = SerializeCore(actualValue, runtimeType, options);
+
+ var output = new BufferWriterBinaryOutput(writer);
+ var context = BinarySerializationContextPool.Get(options);
+ context.Output = output;
+
try
{
- // Apply compression if enabled - compress directly from buffer span (1 allocation)
+ ScanForDuplicates(actualValue, runtimeType, context);
+ context.WriteHeader();
+ WriteValue(actualValue, runtimeType, context, 0);
+
+ // Apply compression if enabled
if (options.UseCompression != Lz4CompressionMode.None)
{
- var compressed = Lz4.Compress(context.AsSpan(), options.UseCompression);
- var destSpan = writer.GetSpan(compressed.Length);
- compressed.CopyTo(destSpan);
- writer.Advance(compressed.Length);
- }
- else
- {
- context.WriteTo(writer);
+ // For compression with BufferWriter, we need to flush first then compress
+ // This path is less common — compression typically uses byte[] path
+ output.Flush();
+ // Compression with IBufferWriter requires intermediate buffer
+ // Fall back to ArrayBinaryOutput path for compression
+ throw new NotSupportedException(
+ "Compression is not supported with IBufferWriter output. " +
+ "Use the byte[] overload or disable compression.");
}
+
+ output.Flush();
}
finally
{
- if (context.Options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
- else BinarySerializationContextPool.Return(context);
+ context.Output = null!;
+ if (options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
+ else BinarySerializationContextPool.Return(context);
}
}
@@ -333,15 +355,24 @@ public static partial class AcBinarySerializer
if (value == null) return 1;
var runtimeType = value.GetType();
- var context = SerializeCore(value, runtimeType, options);
+
+ var context = BinarySerializationContextPool.Get(options);
+ if (context.Output == null)
+ context.Output = new ArrayBinaryOutput(options.InitialBufferCapacity);
+ else
+ context.Output.Reset();
+
try
{
+ ScanForDuplicates(value, runtimeType, context);
+ context.WriteHeader();
+ WriteValue(value, runtimeType, context, 0);
return context.Position;
}
finally
{
- if (context.Options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
- else BinarySerializationContextPool.Return(context);
+ if (options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
+ else BinarySerializationContextPool.Return(context);
}
}
@@ -358,61 +389,54 @@ public static partial class AcBinarySerializer
}
var runtimeType = value.GetType();
- var context = SerializeCore(value, runtimeType, options);
+
+ var context = BinarySerializationContextPool.Get(options);
+ if (context.Output == null)
+ context.Output = new ArrayBinaryOutput(options.InitialBufferCapacity);
+ else
+ context.Output.Reset();
+
try
{
+ ScanForDuplicates(value, runtimeType, context);
+ context.WriteHeader();
+ WriteValue(value, runtimeType, context, 0);
+
// If compression enabled, compress directly from buffer span (1 allocation)
if (options.UseCompression != Lz4CompressionMode.None)
{
- var compressed = Lz4.Compress(context.AsSpan(), options.UseCompression);
+ var compressed = Lz4.Compress(context.Output.AsSpan(), options.UseCompression);
return BinarySerializationResult.FromImmutable(compressed);
}
-
- return context.DetachResult();
+
+ return context.Output.DetachResult();
}
finally
{
- if (context.Options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
- else BinarySerializationContextPool.Return(context);
+ if (options.UseAsync) BinarySerializationContextPool.ReturnAsync(context);
+ else BinarySerializationContextPool.Return(context);
}
}
- private static BinarySerializationContext SerializeCore(object value, Type runtimeType, AcBinarySerializerOptions options)
- {
-#if DEBUG
- BinarySerializationContext.GrowBufferCount = 0;
- BinarySerializationContext.GrowBufferTotalBytes = 0;
-#endif
- var context = BinarySerializationContextPool.Get(options);
-
- // Two-pass serialization:
- // 1. Scan pass: identify duplicates (strings + objects), assign CacheIndex
- // 2. Write header (cacheCount is now known - no placeholder, no shift)
- // 3. Serialize pass: write body with references
- ScanForDuplicates(value, runtimeType, context);
- context.WriteHeader();
- WriteValue(value, runtimeType, context, 0);
-
- return context;
- }
-
#endregion
#region Value Writing
- private static void WriteValue(object? value, Type type, BinarySerializationContext context, int depth)
+ private static void WriteValue(object? value, Type type, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
+ var output = context.Output;
if (value == null)
{
- context.WriteByte(BinaryTypeCode.Null);
+ output.WriteByte(BinaryTypeCode.Null);
return;
}
// Try writing as primitive first
- if (TryWritePrimitive(value, type, context))
+ if (TryWritePrimitive(value, type, output, context))
return;
- WriteValueNonPrimitive(value, type, context, depth);
+ WriteValueNonPrimitive(value, type, output, context, depth);
}
///
@@ -420,26 +444,27 @@ 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.
///
- private static void WriteValueNonPrimitive(object value, Type type, BinarySerializationContext context, int depth)
+ private static void WriteValueNonPrimitive(object value, Type type, TOutput output, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
// Nullable where T is a value type: boxed value may be a primitive.
// Only Nullable can be a value type in the Object accessor path.
if (type.IsValueType)
{
- if (TryWritePrimitive(value, value.GetType(), context))
+ if (TryWritePrimitive(value, value.GetType(), output, context))
return;
}
if (depth > context.MaxDepth)
{
- context.WriteByte(BinaryTypeCode.Null);
+ output.WriteByte(BinaryTypeCode.Null);
return;
}
// Handle byte arrays specially (value-like, no reference tracking)
if (value is byte[] byteArray)
{
- WriteByteArray(byteArray, context);
+ WriteByteArray(byteArray, output);
return;
}
@@ -469,81 +494,82 @@ public static partial class AcBinarySerializer
/// Avoids Nullable.GetUnderlyingType in hot path by using cached type info.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool TryWritePrimitive(object value, Type type, BinarySerializationContext context)
+ private static bool TryWritePrimitive(object value, Type type, TOutput output, BinarySerializationContext context)
+ where TOutput : BinaryOutputBase
{
// Fast path: check TypeCode first (handles most primitives)
var typeCode = Type.GetTypeCode(type);
-
+
switch (typeCode)
{
case TypeCode.Int32:
- WriteInt32((int)value, context);
+ WriteInt32((int)value, output);
return true;
case TypeCode.Int64:
- WriteInt64((long)value, context);
+ WriteInt64((long)value, output);
return true;
case TypeCode.Boolean:
- context.WriteByte((bool)value ? BinaryTypeCode.True : BinaryTypeCode.False);
+ output.WriteByte((bool)value ? BinaryTypeCode.True : BinaryTypeCode.False);
return true;
case TypeCode.Double:
- WriteFloat64Unsafe((double)value, context);
+ WriteFloat64Unsafe((double)value, output);
return true;
case TypeCode.String:
- WriteString((string)value, context);
+ WriteString((string)value, output, context);
return true;
case TypeCode.Single:
- WriteFloat32Unsafe((float)value, context);
+ WriteFloat32Unsafe((float)value, output);
return true;
case TypeCode.Decimal:
- WriteDecimalUnsafe((decimal)value, context);
+ WriteDecimalUnsafe((decimal)value, output);
return true;
case TypeCode.DateTime:
- WriteDateTimeUnsafe((DateTime)value, context);
+ WriteDateTimeUnsafe((DateTime)value, output);
return true;
case TypeCode.Byte:
- context.WriteByte(BinaryTypeCode.UInt8);
- context.WriteByte((byte)value);
+ output.WriteByte(BinaryTypeCode.UInt8);
+ output.WriteByte((byte)value);
return true;
case TypeCode.Int16:
- WriteInt16Unsafe((short)value, context);
+ WriteInt16Unsafe((short)value, output);
return true;
case TypeCode.UInt16:
- WriteUInt16Unsafe((ushort)value, context);
+ WriteUInt16Unsafe((ushort)value, output);
return true;
case TypeCode.UInt32:
- WriteUInt32((uint)value, context);
+ WriteUInt32((uint)value, output);
return true;
case TypeCode.UInt64:
- WriteUInt64((ulong)value, context);
+ WriteUInt64((ulong)value, output);
return true;
case TypeCode.SByte:
- context.WriteByte(BinaryTypeCode.Int8);
- context.WriteByte(unchecked((byte)(sbyte)value));
+ output.WriteByte(BinaryTypeCode.Int8);
+ output.WriteByte(unchecked((byte)(sbyte)value));
return true;
case TypeCode.Char:
- WriteCharUnsafe((char)value, context);
+ WriteCharUnsafe((char)value, output);
return true;
}
// Handle special types by reference comparison (faster than type equality)
if (ReferenceEquals(type, GuidType))
{
- WriteGuidUnsafe((Guid)value, context);
+ WriteGuidUnsafe((Guid)value, output);
return true;
}
if (ReferenceEquals(type, DateTimeOffsetType))
{
- WriteDateTimeOffsetUnsafe((DateTimeOffset)value, context);
+ WriteDateTimeOffsetUnsafe((DateTimeOffset)value, output);
return true;
}
if (ReferenceEquals(type, TimeSpanType))
{
- WriteTimeSpanUnsafe((TimeSpan)value, context);
+ WriteTimeSpanUnsafe((TimeSpan)value, output);
return true;
}
if (type.IsEnum)
{
- WriteEnum(value, context);
+ WriteEnum(value, output);
return true;
}
@@ -553,7 +579,7 @@ public static partial class AcBinarySerializer
{
// When boxed, nullable value types are unwrapped to their underlying type
// So we can just call TryWritePrimitive with the actual runtime type
- return TryWritePrimitive(value, value.GetType(), context);
+ return TryWritePrimitive(value, value.GetType(), output, context);
}
return false;
@@ -564,143 +590,158 @@ public static partial class AcBinarySerializer
#region Optimized Primitive Writers using MemoryMarshal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteInt32(int value, BinarySerializationContext context)
+ private static void WriteInt32(int value, TOutput output)
+ where TOutput : BinaryOutputBase
{
if (BinaryTypeCode.TryEncodeTinyInt(value, out var tiny))
{
- context.WriteByte(tiny);
+ output.WriteByte(tiny);
return;
}
- context.WriteByte(BinaryTypeCode.Int32);
- context.WriteVarInt(value);
+ output.WriteByte(BinaryTypeCode.Int32);
+ output.WriteVarInt(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteInt64(long value, BinarySerializationContext context)
+ private static void WriteInt64(long value, TOutput output)
+ where TOutput : BinaryOutputBase
{
if (value >= int.MinValue && value <= int.MaxValue)
{
- WriteInt32((int)value, context);
+ WriteInt32((int)value, output);
return;
}
- context.WriteByte(BinaryTypeCode.Int64);
- context.WriteVarLong(value);
+ output.WriteByte(BinaryTypeCode.Int64);
+ output.WriteVarLong(value);
}
///
/// Optimized float64 writer using batched write.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteFloat64Unsafe(double value, BinarySerializationContext context)
+ private static void WriteFloat64Unsafe(double value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteTypeCodeAndRaw(BinaryTypeCode.Float64, value);
+ output.WriteTypeCodeAndRaw(BinaryTypeCode.Float64, value);
}
///
/// Optimized float32 writer using batched write.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteFloat32Unsafe(float value, BinarySerializationContext context)
+ private static void WriteFloat32Unsafe(float value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteTypeCodeAndRaw(BinaryTypeCode.Float32, value);
+ output.WriteTypeCodeAndRaw(BinaryTypeCode.Float32, value);
}
///
/// Optimized decimal writer using direct memory copy of bits.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteDecimalUnsafe(decimal value, BinarySerializationContext context)
+ private static void WriteDecimalUnsafe(decimal value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.Decimal);
- context.WriteDecimalBits(value);
+ output.WriteByte(BinaryTypeCode.Decimal);
+ output.WriteDecimalBits(value);
}
///
/// Optimized DateTime writer.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteDateTimeUnsafe(DateTime value, BinarySerializationContext context)
+ private static void WriteDateTimeUnsafe(DateTime value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.DateTime);
- context.WriteDateTimeBits(value);
+ output.WriteByte(BinaryTypeCode.DateTime);
+ output.WriteDateTimeBits(value);
}
///
/// Optimized Guid writer using direct memory copy.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteGuidUnsafe(Guid value, BinarySerializationContext context)
+ private static void WriteGuidUnsafe(Guid value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.Guid);
- context.WriteGuidBits(value);
+ output.WriteByte(BinaryTypeCode.Guid);
+ output.WriteGuidBits(value);
}
///
/// Optimized DateTimeOffset writer.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteDateTimeOffsetUnsafe(DateTimeOffset value, BinarySerializationContext context)
+ private static void WriteDateTimeOffsetUnsafe(DateTimeOffset value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.DateTimeOffset);
- context.WriteDateTimeOffsetBits(value);
+ output.WriteByte(BinaryTypeCode.DateTimeOffset);
+ output.WriteDateTimeOffsetBits(value);
}
///
/// Optimized TimeSpan writer.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteTimeSpanUnsafe(TimeSpan value, BinarySerializationContext context)
+ private static void WriteTimeSpanUnsafe(TimeSpan value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteTypeCodeAndRaw(BinaryTypeCode.TimeSpan, value.Ticks);
+ output.WriteTypeCodeAndRaw(BinaryTypeCode.TimeSpan, value.Ticks);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteInt16Unsafe(short value, BinarySerializationContext context)
+ private static void WriteInt16Unsafe(short value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteTypeCodeAndRaw(BinaryTypeCode.Int16, value);
+ output.WriteTypeCodeAndRaw(BinaryTypeCode.Int16, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteUInt16Unsafe(ushort value, BinarySerializationContext context)
+ private static void WriteUInt16Unsafe(ushort value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteTypeCodeAndRaw(BinaryTypeCode.UInt16, value);
+ output.WriteTypeCodeAndRaw(BinaryTypeCode.UInt16, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteUInt32(uint value, BinarySerializationContext context)
+ private static void WriteUInt32(uint value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.UInt32);
- context.WriteVarUInt(value);
+ output.WriteByte(BinaryTypeCode.UInt32);
+ output.WriteVarUInt(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteUInt64(ulong value, BinarySerializationContext context)
+ private static void WriteUInt64(ulong value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.UInt64);
- context.WriteVarULong(value);
+ output.WriteByte(BinaryTypeCode.UInt64);
+ output.WriteVarULong(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteCharUnsafe(char value, BinarySerializationContext context)
+ private static void WriteCharUnsafe(char value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.Char);
- context.WriteRaw(value);
+ output.WriteByte(BinaryTypeCode.Char);
+ output.WriteRaw(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteEnum(object value, BinarySerializationContext context)
+ private static void WriteEnum(object value, TOutput output)
+ where TOutput : BinaryOutputBase
{
// Use direct unboxing instead of Convert.ToInt32 to avoid NumberFormatInfo overhead
var intValue = GetEnumAsInt32Fast(value);
if (BinaryTypeCode.TryEncodeTinyInt(intValue, out var tiny))
{
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(tiny);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(tiny);
return;
}
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(BinaryTypeCode.Int32);
- context.WriteVarInt(intValue);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(BinaryTypeCode.Int32);
+ output.WriteVarInt(intValue);
}
///
@@ -712,7 +753,7 @@ public static partial class AcBinarySerializer
{
var type = enumValue.GetType();
var underlyingType = type.GetEnumUnderlyingType();
-
+
if (ReferenceEquals(underlyingType, IntType))
return (int)enumValue;
if (ReferenceEquals(underlyingType, typeof(byte)))
@@ -725,7 +766,7 @@ public static partial class AcBinarySerializer
return (ushort)enumValue;
if (ReferenceEquals(underlyingType, typeof(uint)))
return unchecked((int)(uint)enumValue);
-
+
// Fallback for rare cases (long, ulong)
return Convert.ToInt32(enumValue);
}
@@ -735,11 +776,12 @@ public static partial class AcBinarySerializer
/// Marker-based interning: write String marker, rewrite to StringInternFirst at end if needed.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteString(string value, BinarySerializationContext context)
+ private static void WriteString(string value, TOutput output, BinarySerializationContext context)
+ where TOutput : BinaryOutputBase
{
if (value.Length == 0)
{
- context.WriteByte(BinaryTypeCode.StringEmpty);
+ output.WriteByte(BinaryTypeCode.StringEmpty);
return;
}
@@ -760,15 +802,15 @@ public static partial class AcBinarySerializer
{
// 1st serialize occurrence of a cached string - write StringInternFirst + cacheIndex + data
interEntry.IsFirstWrite = false;
- context.WriteByte(BinaryTypeCode.StringInternFirst);
- context.WriteVarUInt((uint)interEntry.CacheIndex);
- context.WriteStringUtf8(value);
+ output.WriteByte(BinaryTypeCode.StringInternFirst);
+ output.WriteVarUInt((uint)interEntry.CacheIndex);
+ output.WriteStringUtf8(value);
}
else
{
// 2+ serialize occurrence: write index reference
- context.WriteByte(BinaryTypeCode.StringInterned);
- context.WriteVarUInt((uint)interEntry.CacheIndex);
+ output.WriteByte(BinaryTypeCode.StringInterned);
+ output.WriteVarUInt((uint)interEntry.CacheIndex);
}
return;
}
@@ -778,8 +820,8 @@ public static partial class AcBinarySerializer
context.OnStringInterned?.Invoke(context.CurrentPropertyPath, value);
#endif
// String not cached (single occurrence or not found) - write plain String
- context.WriteByte(BinaryTypeCode.String);
- context.WriteStringUtf8(value);
+ output.WriteByte(BinaryTypeCode.String);
+ output.WriteStringUtf8(value);
return;
}
@@ -789,29 +831,32 @@ public static partial class AcBinarySerializer
if (length <= BinaryTypeCode.FixStrMaxLength)
{
// For short strings, use direct ASCII copy (avoids double validation)
- context.WriteFixStrDirect(value);
+ output.WriteFixStrDirect(value);
return;
}
// Long strings - standard encoding
- context.WriteByte(BinaryTypeCode.String);
- context.WriteStringUtf8(value);
+ output.WriteByte(BinaryTypeCode.String);
+ output.WriteStringUtf8(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WriteByteArray(byte[] value, BinarySerializationContext context)
+ private static void WriteByteArray(byte[] value, TOutput output)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.ByteArray);
- context.WriteVarUInt((uint)value.Length);
- context.WriteBytes(value);
+ output.WriteByte(BinaryTypeCode.ByteArray);
+ output.WriteVarUInt((uint)value.Length);
+ output.WriteBytes(value);
}
#endregion
#region Complex Type Writers
- private static void WriteObject(object value, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth, bool isNested = false)
+ private static void WriteObject(object value, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth, bool isNested = false)
+ where TOutput : BinaryOutputBase
{
+ var output = context.Output;
var metadata = wrapper.Metadata;
// Wire format:
@@ -824,7 +869,7 @@ public static partial class AcBinarySerializer
var isFirstMetadataOccurrence = false;
if (context.UseMetadata)
{
- isFirstMetadataOccurrence = BinarySerializationContext.RegisterMetadataType(wrapper);
+ isFirstMetadataOccurrence = BinarySerializationContext.RegisterMetadataType(wrapper);
}
// Reference handling: lookup entry from scan pass, check IsFirstWrite
@@ -852,8 +897,8 @@ public static partial class AcBinarySerializer
else
{
// 2+ occurrence → write ObjectRef
- context.WriteByte(BinaryTypeCode.ObjectRef);
- context.WriteVarUInt((uint)entry.CacheIndex);
+ output.WriteByte(BinaryTypeCode.ObjectRef);
+ output.WriteVarUInt((uint)entry.CacheIndex);
return;
}
}
@@ -876,8 +921,8 @@ public static partial class AcBinarySerializer
}
else
{
- context.WriteByte(BinaryTypeCode.ObjectRef);
- context.WriteVarUInt((uint)entry.CacheIndex);
+ output.WriteByte(BinaryTypeCode.ObjectRef);
+ output.WriteVarUInt((uint)entry.CacheIndex);
return;
}
}
@@ -900,8 +945,8 @@ public static partial class AcBinarySerializer
}
else
{
- context.WriteByte(BinaryTypeCode.ObjectRef);
- context.WriteVarUInt((uint)entry.CacheIndex);
+ output.WriteByte(BinaryTypeCode.ObjectRef);
+ output.WriteVarUInt((uint)entry.CacheIndex);
return;
}
}
@@ -911,32 +956,32 @@ public static partial class AcBinarySerializer
}
}
- // Marker kiírása:
+ // Marker kiírása:
// - Cached object first occurrence: ObjectRefFirst/ObjectWithMetadataRefFirst + cacheIndex
// - Non-cached: Object/ObjectWithMetadata
if (context.UseMetadata)
{
if (cachedObjectCacheIndex >= 0)
{
- context.WriteByte(BinaryTypeCode.ObjectWithMetadataRefFirst);
- context.WriteVarUInt((uint)cachedObjectCacheIndex);
+ output.WriteByte(BinaryTypeCode.ObjectWithMetadataRefFirst);
+ output.WriteVarUInt((uint)cachedObjectCacheIndex);
}
else
{
- context.WriteByte(BinaryTypeCode.ObjectWithMetadata);
+ output.WriteByte(BinaryTypeCode.ObjectWithMetadata);
}
- context.WriteInlineMetadata(wrapper.Metadata, isFirstMetadataOccurrence);
+ context.WriteInlineMetadata(wrapper.Metadata, output, isFirstMetadataOccurrence);
}
else
{
if (cachedObjectCacheIndex >= 0)
{
- context.WriteByte(BinaryTypeCode.ObjectRefFirst);
- context.WriteVarUInt((uint)cachedObjectCacheIndex);
+ output.WriteByte(BinaryTypeCode.ObjectRefFirst);
+ output.WriteVarUInt((uint)cachedObjectCacheIndex);
}
else
{
- context.WriteByte(BinaryTypeCode.Object);
+ output.WriteByte(BinaryTypeCode.Object);
}
}
@@ -957,15 +1002,15 @@ public static partial class AcBinarySerializer
if (prop.ExpectedTypeCode.HasValue)
{
- WritePropertyMarkerless(value, prop, context);
+ WritePropertyMarkerless(value, prop, output);
}
else if (hasPropertyFilter && !context.ShouldSerializeProperty(value, prop))
{
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
}
else
{
- WritePropertyOrSkip(value, prop, context, nextDepth);
+ WritePropertyOrSkip(value, prop, output, context, nextDepth);
}
}
}
@@ -978,11 +1023,11 @@ public static partial class AcBinarySerializer
if (hasPropertyFilter && !context.ShouldSerializeProperty(value, prop))
{
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
continue;
}
- WritePropertyOrSkip(value, prop, context, nextDepth);
+ WritePropertyOrSkip(value, prop, output, context, nextDepth);
}
}
}
@@ -1037,62 +1082,63 @@ public static partial class AcBinarySerializer
/// Writes a property value using typed getters to avoid boxing.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WritePropertyValue(object obj, BinaryPropertyAccessor prop, BinarySerializationContext context, int depth)
+ private static void WritePropertyValue(object obj, BinaryPropertyAccessor prop, TOutput output, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
switch (prop.AccessorType)
{
case PropertyAccessorType.Int32:
- WriteInt32(prop.GetInt32(obj), context);
+ WriteInt32(prop.GetInt32(obj), output);
return;
case PropertyAccessorType.Int64:
- WriteInt64(prop.GetInt64(obj), context);
+ WriteInt64(prop.GetInt64(obj), output);
return;
case PropertyAccessorType.Boolean:
- context.WriteByte(prop.GetBoolean(obj) ? BinaryTypeCode.True : BinaryTypeCode.False);
+ output.WriteByte(prop.GetBoolean(obj) ? BinaryTypeCode.True : BinaryTypeCode.False);
return;
case PropertyAccessorType.Double:
- WriteFloat64Unsafe(prop.GetDouble(obj), context);
+ WriteFloat64Unsafe(prop.GetDouble(obj), output);
return;
case PropertyAccessorType.Single:
- WriteFloat32Unsafe(prop.GetSingle(obj), context);
+ WriteFloat32Unsafe(prop.GetSingle(obj), output);
return;
case PropertyAccessorType.Decimal:
- WriteDecimalUnsafe(prop.GetDecimal(obj), context);
+ WriteDecimalUnsafe(prop.GetDecimal(obj), output);
return;
case PropertyAccessorType.DateTime:
- WriteDateTimeUnsafe(prop.GetDateTime(obj), context);
+ WriteDateTimeUnsafe(prop.GetDateTime(obj), output);
return;
case PropertyAccessorType.Byte:
- context.WriteByte(BinaryTypeCode.UInt8);
- context.WriteByte(prop.GetByte(obj));
+ output.WriteByte(BinaryTypeCode.UInt8);
+ output.WriteByte(prop.GetByte(obj));
return;
case PropertyAccessorType.Int16:
- WriteInt16Unsafe(prop.GetInt16(obj), context);
+ WriteInt16Unsafe(prop.GetInt16(obj), output);
return;
case PropertyAccessorType.UInt16:
- WriteUInt16Unsafe(prop.GetUInt16(obj), context);
+ WriteUInt16Unsafe(prop.GetUInt16(obj), output);
return;
case PropertyAccessorType.UInt32:
- WriteUInt32(prop.GetUInt32(obj), context);
+ WriteUInt32(prop.GetUInt32(obj), output);
return;
case PropertyAccessorType.UInt64:
- WriteUInt64(prop.GetUInt64(obj), context);
+ WriteUInt64(prop.GetUInt64(obj), output);
return;
case PropertyAccessorType.Guid:
- WriteGuidUnsafe(prop.GetGuid(obj), context);
+ WriteGuidUnsafe(prop.GetGuid(obj), output);
return;
case PropertyAccessorType.Enum:
var enumValue = prop.GetEnumAsInt32(obj);
if (BinaryTypeCode.TryEncodeTinyInt(enumValue, out var tiny))
{
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(tiny);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(tiny);
}
else
{
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(BinaryTypeCode.Int32);
- context.WriteVarInt(enumValue);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(BinaryTypeCode.Int32);
+ output.WriteVarInt(enumValue);
}
return;
case PropertyAccessorType.String:
@@ -1100,9 +1146,9 @@ public static partial class AcBinarySerializer
// Fast path: typed getter, no boxing, no Type.GetTypeCode() call
var strValue = prop.GetString(obj);
if (strValue != null)
- WriteString(strValue, context);
+ WriteString(strValue, output, context);
else
- context.WriteByte(BinaryTypeCode.Null);
+ output.WriteByte(BinaryTypeCode.Null);
return;
}
default:
@@ -1119,80 +1165,82 @@ public static partial class AcBinarySerializer
/// Avoids double getter calls.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WritePropertyOrSkip(object obj, BinaryPropertyAccessor prop, BinarySerializationContext context, int depth)
+ private static void WritePropertyOrSkip(object obj, BinaryPropertyAccessor prop, TOutput output, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
+ //var output = context.Output;
switch (prop.AccessorType)
{
case PropertyAccessorType.Int32:
{
int value = prop.GetInt32(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteInt32(value, context);
+ WriteInt32(value, output);
return;
}
case PropertyAccessorType.Int64:
{
long value = prop.GetInt64(obj);
if (value == 0L)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteInt64(value, context);
+ WriteInt64(value, output);
return;
}
case PropertyAccessorType.Boolean:
{
bool value = prop.GetBoolean(obj);
if (!value)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- context.WriteByte(BinaryTypeCode.True);
+ output.WriteByte(BinaryTypeCode.True);
return;
}
case PropertyAccessorType.Double:
{
double value = prop.GetDouble(obj);
if (value == 0.0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteFloat64Unsafe(value, context);
+ WriteFloat64Unsafe(value, output);
return;
}
case PropertyAccessorType.Single:
{
float value = prop.GetSingle(obj);
if (value == 0f)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteFloat32Unsafe(value, context);
+ WriteFloat32Unsafe(value, output);
return;
}
case PropertyAccessorType.Decimal:
{
decimal value = prop.GetDecimal(obj);
if (value == 0m)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteDecimalUnsafe(value, context);
+ WriteDecimalUnsafe(value, output);
return;
}
case PropertyAccessorType.DateTime:
{
DateTime value = prop.GetDateTime(obj);
// DateTime always written (no default skip)
- WriteDateTimeUnsafe(value, context);
+ WriteDateTimeUnsafe(value, output);
return;
}
case PropertyAccessorType.Byte:
{
byte value = prop.GetByte(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
{
- context.WriteByte(BinaryTypeCode.UInt8);
- context.WriteByte(value);
+ output.WriteByte(BinaryTypeCode.UInt8);
+ output.WriteByte(value);
}
return;
}
@@ -1200,45 +1248,45 @@ public static partial class AcBinarySerializer
{
short value = prop.GetInt16(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteInt16Unsafe(value, context);
+ WriteInt16Unsafe(value, output);
return;
}
case PropertyAccessorType.UInt16:
{
ushort value = prop.GetUInt16(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteUInt16Unsafe(value, context);
+ WriteUInt16Unsafe(value, output);
return;
}
case PropertyAccessorType.UInt32:
{
uint value = prop.GetUInt32(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteUInt32(value, context);
+ WriteUInt32(value, output);
return;
}
case PropertyAccessorType.UInt64:
{
ulong value = prop.GetUInt64(obj);
if (value == 0)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteUInt64(value, context);
+ WriteUInt64(value, output);
return;
}
case PropertyAccessorType.Guid:
{
Guid value = prop.GetGuid(obj);
if (value == Guid.Empty)
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
else
- WriteGuidUnsafe(value, context);
+ WriteGuidUnsafe(value, output);
return;
}
case PropertyAccessorType.Enum:
@@ -1246,18 +1294,18 @@ public static partial class AcBinarySerializer
int enumValue = prop.GetEnumAsInt32(obj);
if (enumValue == 0)
{
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
}
else if (BinaryTypeCode.TryEncodeTinyInt(enumValue, out var tiny))
{
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(tiny);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(tiny);
}
else
{
- context.WriteByte(BinaryTypeCode.Enum);
- context.WriteByte(BinaryTypeCode.Int32);
- context.WriteVarInt(enumValue);
+ output.WriteByte(BinaryTypeCode.Enum);
+ output.WriteByte(BinaryTypeCode.Int32);
+ output.WriteVarInt(enumValue);
}
return;
}
@@ -1267,14 +1315,14 @@ public static partial class AcBinarySerializer
string? value = prop.GetString(obj);
if (string.IsNullOrEmpty(value))
{
- context.WriteByte(value == null ? BinaryTypeCode.PropertySkip : BinaryTypeCode.StringEmpty);
+ output.WriteByte(value == null ? BinaryTypeCode.PropertySkip : BinaryTypeCode.StringEmpty);
}
else
{
#if DEBUG
context.CurrentPropertyPath = $"{prop.DeclaringType.Name}.{prop.Name}";
#endif
- WriteString(value, context);
+ WriteString(value, output, context);
}
return;
}
@@ -1288,14 +1336,14 @@ public static partial class AcBinarySerializer
// Empty string, empty collections, etc. are valid values and must be written!
if (value == null)
{
- context.WriteByte(BinaryTypeCode.PropertySkip);
+ output.WriteByte(BinaryTypeCode.PropertySkip);
}
else
{
#if DEBUG
context.CurrentPropertyPath = $"{prop.DeclaringType.Name}.{prop.Name}";
#endif
- WriteValueNonPrimitive(value, prop.PropertyType, context, depth);
+ WriteValueNonPrimitive(value, prop.PropertyType, output, context, depth);
}
return;
}
@@ -1308,45 +1356,46 @@ public static partial class AcBinarySerializer
/// Only called for non-nullable value types with ExpectedTypeCode set.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WritePropertyMarkerless(object obj, BinaryPropertyAccessor prop, BinarySerializationContext context)
+ private static void WritePropertyMarkerless(object obj, BinaryPropertyAccessor prop, TOutput output)
+ where TOutput : BinaryOutputBase
{
switch (prop.AccessorType)
{
case PropertyAccessorType.Int32:
- context.WriteVarInt(prop.GetInt32(obj));
+ output.WriteVarInt(prop.GetInt32(obj));
return;
case PropertyAccessorType.Int64:
- context.WriteVarLong(prop.GetInt64(obj));
+ output.WriteVarLong(prop.GetInt64(obj));
return;
case PropertyAccessorType.Double:
- context.WriteRaw(prop.GetDouble(obj));
+ output.WriteRaw(prop.GetDouble(obj));
return;
case PropertyAccessorType.Single:
- context.WriteRaw(prop.GetSingle(obj));
+ output.WriteRaw(prop.GetSingle(obj));
return;
case PropertyAccessorType.Decimal:
- context.WriteDecimalBits(prop.GetDecimal(obj));
+ output.WriteDecimalBits(prop.GetDecimal(obj));
return;
case PropertyAccessorType.DateTime:
- context.WriteDateTimeBits(prop.GetDateTime(obj));
+ output.WriteDateTimeBits(prop.GetDateTime(obj));
return;
case PropertyAccessorType.Guid:
- context.WriteGuidBits(prop.GetGuid(obj));
+ output.WriteGuidBits(prop.GetGuid(obj));
return;
case PropertyAccessorType.Byte:
- context.WriteByte(prop.GetByte(obj));
+ output.WriteByte(prop.GetByte(obj));
return;
case PropertyAccessorType.Int16:
- context.WriteRaw(prop.GetInt16(obj));
+ output.WriteRaw(prop.GetInt16(obj));
return;
case PropertyAccessorType.UInt16:
- context.WriteRaw(prop.GetUInt16(obj));
+ output.WriteRaw(prop.GetUInt16(obj));
return;
case PropertyAccessorType.UInt32:
- context.WriteVarUInt(prop.GetUInt32(obj));
+ output.WriteVarUInt(prop.GetUInt32(obj));
return;
case PropertyAccessorType.UInt64:
- context.WriteVarULong(prop.GetUInt64(obj));
+ output.WriteVarULong(prop.GetUInt64(obj));
return;
}
}
@@ -1358,19 +1407,21 @@ public static partial class AcBinarySerializer
///
/// Optimized array writer with specialized paths for primitive arrays.
///
- private static void WriteArray(IEnumerable enumerable, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth)
+ private static void WriteArray(IEnumerable enumerable, TypeMetadataWrapper wrapper, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.Array);
+ var output = context.Output;
+ output.WriteByte(BinaryTypeCode.Array);
var nextDepth = depth + 1;
// Use pre-computed metadata — no GetWrapper or GetCollectionElementType needed
var metadata = wrapper.Metadata;
var elementType = metadata.CollectionElementType;
-
+
// Optimized path for primitive arrays
if (elementType != null && metadata.SourceType.IsArray)
{
- if (TryWritePrimitiveArray(enumerable, elementType, context))
+ if (TryWritePrimitiveArray(enumerable, elementType, output, context))
return;
}
@@ -1378,7 +1429,7 @@ public static partial class AcBinarySerializer
if (enumerable is IList list)
{
var count = list.Count;
- context.WriteVarUInt((uint)count);
+ output.WriteVarUInt((uint)count);
for (var i = 0; i < count; i++)
{
var item = list[i];
@@ -1395,7 +1446,7 @@ public static partial class AcBinarySerializer
items.Add(item);
}
- context.WriteVarUInt((uint)items.Count);
+ output.WriteVarUInt((uint)items.Count);
foreach (var item in items)
{
var itemType = item?.GetType() ?? typeof(object);
@@ -1405,70 +1456,96 @@ public static partial class AcBinarySerializer
///
/// Specialized array writer for primitive arrays using bulk memory operations.
- /// Optimized for Blazor Hybrid compatibility (WASM, Android, Windows, iOS).
+ /// Non-generic to avoid JIT code duplication — virtual dispatch cost is negligible
+ /// because this is called once per array, not per element.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool TryWritePrimitiveArray(IEnumerable enumerable, Type elementType, BinarySerializationContext context)
+ private static bool TryWritePrimitiveArray(IEnumerable enumerable, Type elementType, TOutput output, BinarySerializationContext context)
+ where TOutput : BinaryOutputBase
+ {
+ // String array needs context for interning — keep generic path
+ if (ReferenceEquals(elementType, StringType) && enumerable is string[] stringArray)
+ {
+ output.WriteVarUInt((uint)stringArray.Length);
+ for (var i = 0; i < stringArray.Length; i++)
+ {
+ var s = stringArray[i];
+ if (s == null)
+ output.WriteByte(BinaryTypeCode.Null);
+ else
+ WriteString(s, output, context);
+ }
+ return true;
+ }
+
+ // All other primitive arrays don't need context — dispatch through base class
+ return TryWritePrimitiveArrayCore(enumerable, elementType, output);
+ }
+
+ ///
+ /// Non-generic core for primitive array writes. Only compiled once by JIT (not per TOutput).
+ /// Virtual dispatch on BinaryOutputBase is negligible: one vtable lookup per array, not per element.
+ ///
+ private static bool TryWritePrimitiveArrayCore(IEnumerable enumerable, Type elementType, BinaryOutputBase output)
{
// Int32 array - very common case
if (ReferenceEquals(elementType, IntType) && enumerable is int[] intArray)
{
- context.WriteVarUInt((uint)intArray.Length);
- context.WriteInt32ArrayOptimized(intArray);
+ output.WriteVarUInt((uint)intArray.Length);
+ output.WriteInt32ArrayOptimized(intArray);
return true;
}
// Double array - bulk write as raw bytes
if (ReferenceEquals(elementType, DoubleType) && enumerable is double[] doubleArray)
{
- context.WriteVarUInt((uint)doubleArray.Length);
- context.WriteDoubleArrayBulk(doubleArray);
+ output.WriteVarUInt((uint)doubleArray.Length);
+ output.WriteDoubleArrayBulk(doubleArray);
return true;
}
// Long array
if (ReferenceEquals(elementType, LongType) && enumerable is long[] longArray)
{
- context.WriteVarUInt((uint)longArray.Length);
- context.WriteLongArrayOptimized(longArray);
+ output.WriteVarUInt((uint)longArray.Length);
+ output.WriteLongArrayOptimized(longArray);
return true;
}
// Float array - bulk write as raw bytes
if (ReferenceEquals(elementType, FloatType) && enumerable is float[] floatArray)
{
- context.WriteVarUInt((uint)floatArray.Length);
- context.WriteFloatArrayBulk(floatArray);
+ output.WriteVarUInt((uint)floatArray.Length);
+ output.WriteFloatArrayBulk(floatArray);
return true;
}
// Bool array - pack as bytes
if (ReferenceEquals(elementType, BoolType) && enumerable is bool[] boolArray)
{
- context.WriteVarUInt((uint)boolArray.Length);
+ output.WriteVarUInt((uint)boolArray.Length);
for (var i = 0; i < boolArray.Length; i++)
{
- context.WriteByte(boolArray[i] ? BinaryTypeCode.True : BinaryTypeCode.False);
+ output.WriteByte(boolArray[i] ? BinaryTypeCode.True : BinaryTypeCode.False);
}
- context.WriteVarUInt((uint)boolArray.Length);
+ output.WriteVarUInt((uint)boolArray.Length);
return true;
}
// Guid array - bulk write
if (ReferenceEquals(elementType, GuidType) && enumerable is Guid[] guidArray)
{
- context.WriteVarUInt((uint)guidArray.Length);
- context.WriteGuidArrayBulk(guidArray);
+ output.WriteVarUInt((uint)guidArray.Length);
+ output.WriteGuidArrayBulk(guidArray);
return true;
}
// Decimal array
if (ReferenceEquals(elementType, DecimalType) && enumerable is decimal[] decimalArray)
{
- context.WriteVarUInt((uint)decimalArray.Length);
+ output.WriteVarUInt((uint)decimalArray.Length);
for (var i = 0; i < decimalArray.Length; i++)
{
- WriteDecimalUnsafe(decimalArray[i], context);
+ WriteDecimalUnsafe(decimalArray[i], output);
}
return true;
}
@@ -1476,25 +1553,11 @@ public static partial class AcBinarySerializer
// DateTime array
if (ReferenceEquals(elementType, DateTimeType) && enumerable is DateTime[] dateTimeArray)
{
- context.WriteVarUInt((uint)dateTimeArray.Length);
+ output.WriteVarUInt((uint)dateTimeArray.Length);
for (var i = 0; i < dateTimeArray.Length; i++)
{
- WriteDateTimeUnsafe(dateTimeArray[i], context);
- }
- return true;
- }
+ WriteDateTimeUnsafe(dateTimeArray[i], output);
- // String array - common case
- if (ReferenceEquals(elementType, StringType) && enumerable is string[] stringArray)
- {
- context.WriteVarUInt((uint)stringArray.Length);
- for (var i = 0; i < stringArray.Length; i++)
- {
- var s = stringArray[i];
- if (s == null)
- context.WriteByte(BinaryTypeCode.Null);
- else
- WriteString(s, context);
}
return true;
}
@@ -1502,10 +1565,12 @@ public static partial class AcBinarySerializer
return false;
}
- private static void WriteDictionary(IDictionary dictionary, BinarySerializationContext context, int depth)
+ private static void WriteDictionary(IDictionary dictionary, BinarySerializationContext context, int depth)
+ where TOutput : BinaryOutputBase
{
- context.WriteByte(BinaryTypeCode.Dictionary);
- context.WriteVarUInt((uint)dictionary.Count);
+ var output = context.Output;
+ output.WriteByte(BinaryTypeCode.Dictionary);
+ output.WriteVarUInt((uint)dictionary.Count);
var nextDepth = depth + 1;
foreach (DictionaryEntry entry in dictionary)
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
index 624babf..f52489b 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializerOptions.cs
@@ -82,7 +82,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
/// allowing the deserializer to match properties by name between different types.
/// Default: false (no overhead)
///
- public bool UseMetadata { get; set; } = true;
+ public bool UseMetadata { get; set; } = false;
///
/// When true, checks for duplicate property name hashes during serialization (UseMetadata mode).
diff --git a/AyCode.Core/Serializers/Binaries/ArrayBinaryOutput.cs b/AyCode.Core/Serializers/Binaries/ArrayBinaryOutput.cs
index b205d12..c8b5e80 100644
--- a/AyCode.Core/Serializers/Binaries/ArrayBinaryOutput.cs
+++ b/AyCode.Core/Serializers/Binaries/ArrayBinaryOutput.cs
@@ -398,6 +398,21 @@ public sealed class ArrayBinaryOutput : BinaryOutputBase, IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() => _position = 0;
+ ///
+ /// Detaches the internal buffer as a BinarySerializationResult and allocates a fresh buffer.
+ /// The caller owns the returned result and must dispose it to return the buffer to the pool.
+ ///
+ public AcBinarySerializer.BinarySerializationResult DetachResult()
+ {
+ var resultBuffer = _buffer;
+ var resultLength = _position;
+
+ _buffer = ArrayPool.Shared.Rent(Math.Max(resultBuffer.Length / 2, MinBufferSize));
+ _position = 0;
+
+ return new AcBinarySerializer.BinarySerializationResult(resultBuffer, resultLength, pooled: true);
+ }
+
#endregion
#region IDisposable
diff --git a/AyCode.Core/Serializers/Binaries/BinaryOutputBase.cs b/AyCode.Core/Serializers/Binaries/BinaryOutputBase.cs
index 95aaeec..829b220 100644
--- a/AyCode.Core/Serializers/Binaries/BinaryOutputBase.cs
+++ b/AyCode.Core/Serializers/Binaries/BinaryOutputBase.cs
@@ -47,82 +47,26 @@ public abstract class BinaryOutputBase : IBinaryOutput
#endregion
- #region Virtual — WriteTypeCodeAndRaw
+ #region Abstract — WriteTypeCodeAndRaw
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public virtual void WriteTypeCodeAndRaw(byte typeCode, T value) where T : unmanaged
- {
- EnsureCapacity(1 + Unsafe.SizeOf());
- WriteByte(typeCode);
- WriteRaw(value);
- }
+ public abstract void WriteTypeCodeAndRaw(byte typeCode, T value) where T : unmanaged;
#endregion
- #region Virtual — VarInt Encoding
+ #region Abstract — VarInt Encoding
///
- public virtual void WriteVarInt(int value)
- {
- var encoded = (uint)((value << 1) ^ (value >> 31));
- WriteVarUInt(encoded);
- }
+ public abstract void WriteVarInt(int value);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public virtual void WriteVarUInt(uint value)
- {
- if (value < 0x80)
- {
- WriteByte((byte)value);
- return;
- }
-
- WriteVarUIntMultiByte(value);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void WriteVarUIntMultiByte(uint value)
- {
- while (value >= 0x80)
- {
- WriteByte((byte)(value | 0x80));
- value >>= 7;
- }
- WriteByte((byte)value);
- }
+ public abstract void WriteVarUInt(uint value);
///
- public virtual void WriteVarLong(long value)
- {
- var encoded = (ulong)((value << 1) ^ (value >> 63));
- WriteVarULong(encoded);
- }
+ public abstract void WriteVarLong(long value);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public virtual void WriteVarULong(ulong value)
- {
- if (value < 0x80)
- {
- WriteByte((byte)value);
- return;
- }
-
- WriteVarULongMultiByte(value);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void WriteVarULongMultiByte(ulong value)
- {
- while (value >= 0x80)
- {
- WriteByte((byte)(value | 0x80));
- value >>= 7;
- }
- WriteByte((byte)value);
- }
+ public abstract void WriteVarULong(ulong value);
#endregion
@@ -137,11 +81,7 @@ public abstract class BinaryOutputBase : IBinaryOutput
}
///
- public virtual void WriteDateTimeBits(DateTime value)
- {
- WriteRaw(value.Ticks);
- WriteByte((byte)value.Kind);
- }
+ public abstract void WriteDateTimeBits(DateTime value);
///
public virtual void WriteGuidBits(Guid value)
@@ -152,11 +92,7 @@ public abstract class BinaryOutputBase : IBinaryOutput
}
///
- public virtual void WriteDateTimeOffsetBits(DateTimeOffset value)
- {
- WriteRaw(value.UtcTicks);
- WriteRaw((short)value.Offset.TotalMinutes);
- }
+ public abstract void WriteDateTimeOffsetBits(DateTimeOffset value);
#endregion
@@ -247,39 +183,16 @@ public abstract class BinaryOutputBase : IBinaryOutput
#endregion
- #region Virtual — Bulk Array Writes (overridable for optimization)
+ #region Bulk Array Writes
///
- public virtual void WriteDoubleArrayBulk(double[] array)
- {
- for (var i = 0; i < array.Length; i++)
- {
- WriteByte(BinaryTypeCode.Float64);
- WriteRaw(array[i]);
- }
- }
+ public abstract void WriteDoubleArrayBulk(double[] array);
///
- public virtual void WriteFloatArrayBulk(float[] array)
- {
- for (var i = 0; i < array.Length; i++)
- {
- WriteByte(BinaryTypeCode.Float32);
- WriteRaw(array[i]);
- }
- }
+ public abstract void WriteFloatArrayBulk(float[] array);
///
- public virtual void WriteGuidArrayBulk(Guid[] array)
- {
- Span buf = stackalloc byte[16];
- for (var i = 0; i < array.Length; i++)
- {
- WriteByte(BinaryTypeCode.Guid);
- array[i].TryWriteBytes(buf);
- WriteBytes(buf);
- }
- }
+ public abstract void WriteGuidArrayBulk(Guid[] array);
///
public virtual void WriteInt32ArrayOptimized(int[] array)