Refactor AcBinarySerializer to use declared type dispatch

- All serialization APIs now use the declared type (typeof(T) or explicit Type) for dispatch, not value.GetType()
- Added non-generic overloads for Serialize, SerializeChunked, and SerializeChunkedFramed with Type parameter for runtime scenarios
- ScanForDuplicates accepts optional TypeMetadataWrapper to avoid redundant lookups
- Simplified generated writer path and improved wrapper usage
- Benchmarks updated to use new API and cache serialized data
- Minor cleanups: removed unused usings, improved comments, inlined logic
- Ensures consistent, predictable, and more performant type dispatch across all serialization entry points
This commit is contained in:
Loretta 2026-05-26 07:56:25 +02:00
parent d4e4c4480a
commit cf92370bea
4 changed files with 52 additions and 98 deletions

View File

@ -29,6 +29,7 @@ public sealed class AcBinaryBenchmark<T> : ISerializerBenchmark where T : class
_order = order; _order = order;
_options = options; _options = options;
OptionsPreset = optionsPreset; OptionsPreset = optionsPreset;
_serialized = AcBinarySerializer.Serialize(order, options); _serialized = AcBinarySerializer.Serialize(order, options);
} }
@ -42,6 +43,7 @@ public sealed class AcBinaryBenchmark<T> : ISerializerBenchmark where T : class
{ {
var bytes = AcBinarySerializer.Serialize(_order, _options); var bytes = AcBinarySerializer.Serialize(_order, _options);
var roundTripped = AcBinaryDeserializer.Deserialize<T>(bytes, _options); var roundTripped = AcBinaryDeserializer.Deserialize<T>(bytes, _options);
return RoundTripValidator.DeepEqualsViaJson(_order, roundTripped); return RoundTripValidator.DeepEqualsViaJson(_order, roundTripped);
} }
} }

View File

@ -31,6 +31,7 @@ public sealed class AcBinaryBufferWriterBenchmark<T> : ISerializerBenchmark wher
_order = order; _order = order;
_options = options; _options = options;
OptionsPreset = optionsPreset; OptionsPreset = optionsPreset;
_serialized = AcBinarySerializer.Serialize(order, options); _serialized = AcBinarySerializer.Serialize(order, options);
// Measure ONLY the BufferWriter infrastructure setup on the serialize side (excluding the // Measure ONLY the BufferWriter infrastructure setup on the serialize side (excluding the

View File

@ -1,6 +1,5 @@
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using static AyCode.Core.Helpers.JsonUtilities;
namespace AyCode.Core.Serializers.Binaries; namespace AyCode.Core.Serializers.Binaries;
@ -18,25 +17,20 @@ public static partial class AcBinarySerializer
/// so no dictionary lookup overhead. SGen types call generated ScanForDuplicates /// so no dictionary lookup overhead. SGen types call generated ScanForDuplicates
/// which bypasses the entire runtime scan path. /// which bypasses the entire runtime scan path.
/// </summary> /// </summary>
private static void ScanForDuplicates<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context) private static void ScanForDuplicates<TOutput>(object value, Type type, BinarySerializationContext<TOutput> context, TypeMetadataWrapper<BinarySerializeTypeMetadata>? wrapper = null)
where TOutput : struct, IBinaryOutputBase where TOutput : struct, IBinaryOutputBase
{ {
if (!context.HasCaching) if (!context.HasCaching) return;
return;
var wrapper = context.GetWrapper(type); wrapper ??= context.GetWrapper(type);
// SGen path: wrapper.GeneratedWriter is cached (no registry lookup per call). // SGen path: wrapper.GeneratedWriter is cached (no registry lookup per call).
// Generated ScanForDuplicates handles HasCaching + ScanObject + SortWritePlan. // Generated ScanForDuplicates handles HasCaching + ScanObject + SortWritePlan.
var genWriter = wrapper.GeneratedWriter; var genWriter = wrapper.GeneratedWriter;
if (genWriter != null && context.Options.UseGeneratedCode)
{
genWriter.ScanObject(value, context);
context.SortWritePlan();
return;
}
ScanValue(value, wrapper, context); if (genWriter != null && context.Options.UseGeneratedCode) genWriter.ScanObject(value, context);
else ScanValue(value, wrapper, context);
context.SortWritePlan(); context.SortWritePlan();
} }

View File

@ -281,8 +281,7 @@ public static partial class AcBinarySerializer
internal static void Register(Type type, IGeneratedBinaryWriter writer) => Writers[type] = writer; internal static void Register(Type type, IGeneratedBinaryWriter writer) => Writers[type] = writer;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static IGeneratedBinaryWriter? TryGet(Type type) => internal static IGeneratedBinaryWriter? TryGet(Type type) => Writers.GetValueOrDefault(type);
Writers.TryGetValue(type, out var writer) ? writer : null;
} }
/// <summary> /// <summary>
@ -312,85 +311,40 @@ public static partial class AcBinarySerializer
/// Uses ArrayBinaryOutput for byte[] result path. /// Uses ArrayBinaryOutput for byte[] result path.
/// </summary> /// </summary>
public static byte[] Serialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, AcBinarySerializerOptions options) public static byte[] Serialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, AcBinarySerializerOptions options)
{ => Serialize(value, typeof(T), options);
if (value == null) return [BinaryTypeCode.Null];
var runtimeType = value.GetType(); public static byte[] Serialize(object? value, AcBinarySerializerOptions options)
var context = AcquireArrayOutputContext(options); => value == null ? [BinaryTypeCode.Null] : Serialize(value, value.GetType(), options);
try
{
// SGen fast path: skip IQueryable/Expression check + WriteValue dispatch chain.
// If root type has a GeneratedWriter it cannot be IQueryable/Expression/primitive/collection.
if (options.UseGeneratedCode)
{
var wrapper = context.GetWrapper(runtimeType);
if (wrapper.GeneratedWriter != null)
{
ScanForDuplicates(value, runtimeType, context);
context.WriteHeader();
WriteObject(value, wrapper, context);
if (options.UseCompression != Lz4CompressionMode.None)
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
return context.Output.ToArray(context._buffer, context._position);
}
}
// Full path: IQueryable/Expression conversion, primitive/collection dispatch
var actualValue = ConvertExpressionValue(value, ref runtimeType);
ScanForDuplicates(actualValue, runtimeType, context);
context.WriteHeader();
WriteValue(actualValue, runtimeType, context);
if (options.UseCompression != Lz4CompressionMode.None)
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
return context.Output.ToArray(context._buffer, context._position);
}
finally
{
ReturnContext(context, options);
}
}
/// <summary> /// <summary>
/// Non-generic <c>Type</c>-based <see cref="Serialize{T}(T, AcBinarySerializerOptions)"/>. For /// Non-generic <c>Type</c>-based <see cref="Serialize{T}(T, AcBinarySerializerOptions)"/>. For
/// runtime-typed scenarios (plugin frameworks, ASP.NET ModelBinding, MVC formatters). The /// runtime-typed scenarios (plugin frameworks, ASP.NET ModelBinding, MVC formatters). The
/// <paramref name="type"/> parameter is the declared-type hint; the body uses /// <paramref name="type"/> parameter is the declared type used for dispatch — identical semantics
/// <c>value.GetType()</c> for the runtime polymorphism path, identical to the generic version. /// to the generic version where <c>typeof(T)</c> is the dispatch type.
/// </summary> /// </summary>
public static byte[] Serialize(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, AcBinarySerializerOptions options) public static byte[] Serialize(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, AcBinarySerializerOptions options)
{ {
if (value == null) return [BinaryTypeCode.Null]; if (value == null) return [BinaryTypeCode.Null];
var runtimeType = value.GetType();
var context = AcquireArrayOutputContext(options); var context = AcquireArrayOutputContext(options);
try try
{ {
if (options.UseGeneratedCode) // Full path: IQueryable/Expression conversion, primitive/collection dispatch
{ var actualValue = value; //ConvertExpressionValue(value, ref runtimeType);
var wrapper = context.GetWrapper(runtimeType); var wrapper = context.GetWrapper(type);
if (wrapper.GeneratedWriter != null)
{
ScanForDuplicates(value, runtimeType, context);
context.WriteHeader();
WriteObject(value, wrapper, context);
if (options.UseCompression != Lz4CompressionMode.None) ScanForDuplicates(actualValue, type, context, wrapper);
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression);
return context.Output.ToArray(context._buffer, context._position);
}
}
var actualValue = ConvertExpressionValue(value, ref runtimeType);
ScanForDuplicates(actualValue, runtimeType, context);
context.WriteHeader(); context.WriteHeader();
WriteValue(actualValue, runtimeType, context);
if (options.UseCompression != Lz4CompressionMode.None) // SGen fast path: skip IQueryable/Expression check + WriteValue dispatch chain.
return Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression); // If root type has a GeneratedWriter it cannot be IQueryable/Expression/primitive/collection.
return context.Output.ToArray(context._buffer, context._position); if (wrapper.GeneratedWriter != null && options.UseGeneratedCode) WriteObject(actualValue, wrapper, context);
else WriteValue(actualValue, type, context);
return options.UseCompression != Lz4CompressionMode.None
? Lz4.Compress(context.Output.AsSpan(context._buffer, context._position), options.UseCompression)
: context.Output.ToArray(context._buffer, context._position);
} }
finally finally
{ {
@ -405,8 +359,10 @@ public static partial class AcBinarySerializer
internal static void ScanOnly<T>(T value, AcBinarySerializerOptions options) internal static void ScanOnly<T>(T value, AcBinarySerializerOptions options)
{ {
if (value == null) return; if (value == null) return;
var runtimeType = value.GetType();
var runtimeType = typeof(T);
var context = AcquireArrayOutputContext(options); var context = AcquireArrayOutputContext(options);
try try
{ {
ScanForDuplicates(value, runtimeType, context); ScanForDuplicates(value, runtimeType, context);
@ -432,8 +388,9 @@ public static partial class AcBinarySerializer
return 1; return 1;
} }
var runtimeType = value.GetType(); var runtimeType = typeof(T);
var context = BinarySerializationContextPool<BufferWriterBinaryOutput>.Get(options); var context = BinarySerializationContextPool<BufferWriterBinaryOutput>.Get(options);
context.Output = new BufferWriterBinaryOutput(writer, options.BufferWriterChunkSize); context.Output = new BufferWriterBinaryOutput(writer, options.BufferWriterChunkSize);
context.Output.Initialize(out context._buffer, out context._position, out context._bufferEnd); context.Output.Initialize(out context._buffer, out context._position, out context._bufferEnd);
@ -494,7 +451,7 @@ public static partial class AcBinarySerializer
return 1; return 1;
} }
var runtimeType = value.GetType(); var runtimeType = type;
var context = BinarySerializationContextPool<BufferWriterBinaryOutput>.Get(options); var context = BinarySerializationContextPool<BufferWriterBinaryOutput>.Get(options);
context.Output = new BufferWriterBinaryOutput(writer, options.BufferWriterChunkSize); context.Output = new BufferWriterBinaryOutput(writer, options.BufferWriterChunkSize);
context.Output.Initialize(out context._buffer, out context._position, out context._bufferEnd); context.Output.Initialize(out context._buffer, out context._position, out context._bufferEnd);
@ -568,11 +525,13 @@ public static partial class AcBinarySerializer
/// </param> /// </param>
/// <returns>Total serialized bytes written.</returns> /// <returns>Total serialized bytes written.</returns>
public static int SerializeChunked<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.Pipe pipe, AcBinarySerializerOptions options, FlushPolicy flushPolicy = FlushPolicy.DoubleBuffered, TimeSpan? flushTimeout = null) public static int SerializeChunked<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.Pipe pipe, AcBinarySerializerOptions options, FlushPolicy flushPolicy = FlushPolicy.DoubleBuffered, TimeSpan? flushTimeout = null)
{ => SerializeToPipeWriterCore(value, typeof(T), pipe.Writer, options, flushPolicy, flushTimeout, multiMessage: false);
if (pipe is null) throw new ArgumentNullException(nameof(pipe));
return SerializeToPipeWriterCore(value, pipe.Writer, options, flushPolicy, flushTimeout, multiMessage: false);
}
// SerializeChunked non-generic
public static int SerializeChunked(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, System.IO.Pipelines.Pipe pipe, AcBinarySerializerOptions options, FlushPolicy flushPolicy = FlushPolicy.DoubleBuffered, TimeSpan? flushTimeout = null)
=> SerializeToPipeWriterCore(value, type, pipe.Writer, options, flushPolicy, flushTimeout, multiMessage: false);
/// <summary> /// <summary>
/// Serialize to any <see cref="System.IO.Pipelines.PipeWriter"/> as a chunked stream — pure /// Serialize to any <see cref="System.IO.Pipelines.PipeWriter"/> as a chunked stream — pure
/// AcBinary bytes, no per-chunk header. The output is byte-compatible with /// AcBinary bytes, no per-chunk header. The output is byte-compatible with
@ -597,7 +556,7 @@ public static partial class AcBinarySerializer
/// <param name="options">Serializer options (type wrappers, reference handling, interning, etc.).</param> /// <param name="options">Serializer options (type wrappers, reference handling, interning, etc.).</param>
/// <returns>Total serialized bytes written.</returns> /// <returns>Total serialized bytes written.</returns>
public static int SerializeChunked<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options) public static int SerializeChunked<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options)
=> SerializeToPipeWriterCore(value, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: false); => SerializeToPipeWriterCore(value, typeof(T), pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: false);
/// <summary> /// <summary>
/// Non-generic <c>Type</c>-based counterpart to /// Non-generic <c>Type</c>-based counterpart to
@ -605,7 +564,7 @@ public static partial class AcBinarySerializer
/// For runtime-typed scenarios (MVC formatters, plugin frameworks). /// For runtime-typed scenarios (MVC formatters, plugin frameworks).
/// </summary> /// </summary>
public static int SerializeChunked(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options) public static int SerializeChunked(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options)
=> SerializeToPipeWriterCore<object?>(value, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: false); => SerializeToPipeWriterCore(value,type, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: false);
/// <summary> /// <summary>
/// Serialize a value into a chunked stream where each chunk carries a self-describing /// Serialize a value into a chunked stream where each chunk carries a self-describing
@ -635,10 +594,8 @@ public static partial class AcBinarySerializer
/// <param name="flushTimeout">See <see cref="SerializeChunked{T}(T, System.IO.Pipelines.Pipe, AcBinarySerializerOptions, FlushPolicy, TimeSpan?)"/>.</param> /// <param name="flushTimeout">See <see cref="SerializeChunked{T}(T, System.IO.Pipelines.Pipe, AcBinarySerializerOptions, FlushPolicy, TimeSpan?)"/>.</param>
/// <returns>Total serialized data bytes (excluding framing overhead).</returns> /// <returns>Total serialized data bytes (excluding framing overhead).</returns>
public static int SerializeChunkedFramed<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.Pipe pipe, AcBinarySerializerOptions options, FlushPolicy flushPolicy = FlushPolicy.DoubleBuffered, TimeSpan? flushTimeout = null) public static int SerializeChunkedFramed<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.Pipe pipe, AcBinarySerializerOptions options, FlushPolicy flushPolicy = FlushPolicy.DoubleBuffered, TimeSpan? flushTimeout = null)
{ => SerializeToPipeWriterCore(value, typeof(T), pipe.Writer, options, flushPolicy, flushTimeout, multiMessage: true);
if (pipe is null) throw new ArgumentNullException(nameof(pipe));
return SerializeToPipeWriterCore(value, pipe.Writer, options, flushPolicy, flushTimeout, multiMessage: true);
}
/// <summary> /// <summary>
/// Serialize to any <see cref="System.IO.Pipelines.PipeWriter"/> with per-chunk frame headers /// Serialize to any <see cref="System.IO.Pipelines.PipeWriter"/> with per-chunk frame headers
@ -650,14 +607,14 @@ public static partial class AcBinarySerializer
/// <see cref="SerializeChunked{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions)"/>.</para> /// <see cref="SerializeChunked{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions)"/>.</para>
/// </summary> /// </summary>
public static int SerializeChunkedFramed<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options) public static int SerializeChunkedFramed<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options)
=> SerializeToPipeWriterCore(value, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: true); => SerializeToPipeWriterCore(value, typeof(T), pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: true);
/// <summary> /// <summary>
/// Non-generic <c>Type</c>-based counterpart to /// Non-generic <c>Type</c>-based counterpart to
/// <see cref="SerializeChunkedFramed{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions)"/>. /// <see cref="SerializeChunkedFramed{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions)"/>.
/// </summary> /// </summary>
public static int SerializeChunkedFramed(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options) public static int SerializeChunkedFramed(object? value, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options)
=> SerializeToPipeWriterCore<object?>(value, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: true); => SerializeToPipeWriterCore(value, type, pipeWriter, options, FlushPolicy.DoubleBuffered, flushTimeout: null, multiMessage: true);
/// <summary> /// <summary>
/// Internal flush-tunable framed PipeWriter overload — used by <c>AyCode.Services</c> /// Internal flush-tunable framed PipeWriter overload — used by <c>AyCode.Services</c>
@ -666,7 +623,7 @@ public static partial class AcBinarySerializer
/// <paramref name="flushPolicy"/> on a guaranteed parallel-capable writer. /// <paramref name="flushPolicy"/> on a guaranteed parallel-capable writer.
/// </summary> /// </summary>
internal static int SerializeChunkedFramed<T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout) internal static int SerializeChunkedFramed<T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout)
=> SerializeToPipeWriterCore(value, pipeWriter, options, flushPolicy, flushTimeout, multiMessage: true); => SerializeToPipeWriterCore(value, typeof(T), pipeWriter, options, flushPolicy, flushTimeout, multiMessage: true);
/// <summary> /// <summary>
/// Internal legacy alias for <see cref="SerializeChunkedFramed{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions, FlushPolicy, TimeSpan?)"/> /// Internal legacy alias for <see cref="SerializeChunkedFramed{T}(T, System.IO.Pipelines.PipeWriter, AcBinarySerializerOptions, FlushPolicy, TimeSpan?)"/>
@ -675,14 +632,14 @@ public static partial class AcBinarySerializer
/// (framed wire format with <c>[201][UINT16][data]</c> per chunk + <c>[202]</c> end marker). /// (framed wire format with <c>[201][UINT16][data]</c> per chunk + <c>[202]</c> end marker).
/// </summary> /// </summary>
internal static int Serialize<T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout) internal static int Serialize<T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout)
=> SerializeToPipeWriterCore(value, pipeWriter, options, flushPolicy, flushTimeout, multiMessage: true); => SerializeToPipeWriterCore(value, typeof(T), pipeWriter, options, flushPolicy, flushTimeout, multiMessage: true);
/// <summary> /// <summary>
/// Common pipe-output serialization core. Same loop for both raw (<see cref="SerializeChunked{T}"/>) /// Common pipe-output serialization core. Same loop for both raw (<see cref="SerializeChunked{T}"/>)
/// and framed (<see cref="SerializeChunkedFramed{T}"/>) modes — the only difference flows through /// and framed (<see cref="SerializeChunkedFramed{T}"/>) modes — the only difference flows through
/// <paramref name="multiMessage"/> into the <see cref="AsyncPipeWriterOutput"/> ctor. /// <paramref name="multiMessage"/> into the <see cref="AsyncPipeWriterOutput"/> ctor.
/// </summary> /// </summary>
private static int SerializeToPipeWriterCore<T>(T value, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout, bool multiMessage) private static int SerializeToPipeWriterCore(object? value, Type type, System.IO.Pipelines.PipeWriter pipeWriter, AcBinarySerializerOptions options, FlushPolicy flushPolicy, TimeSpan? flushTimeout, bool multiMessage)
{ {
if (value == null) if (value == null)
{ {
@ -707,7 +664,7 @@ public static partial class AcBinarySerializer
return 1; return 1;
} }
var runtimeType = value.GetType(); var runtimeType = type;
var context = BinarySerializationContextPool<AsyncPipeWriterOutput>.Get(options); var context = BinarySerializationContextPool<AsyncPipeWriterOutput>.Get(options);
context.Output = new AsyncPipeWriterOutput(pipeWriter, options.BufferWriterChunkSize, multiMessage, flushPolicy, flushTimeout); context.Output = new AsyncPipeWriterOutput(pipeWriter, options.BufferWriterChunkSize, multiMessage, flushPolicy, flushTimeout);
@ -760,7 +717,7 @@ public static partial class AcBinarySerializer
{ {
if (value == null) return 1; if (value == null) return 1;
var runtimeType = value.GetType(); var runtimeType = typeof(T);
var context = AcquireArrayOutputContext(options); var context = AcquireArrayOutputContext(options);
try try
@ -785,7 +742,7 @@ public static partial class AcBinarySerializer
{ {
if (value == null) return BinarySerializationResult.FromImmutable([BinaryTypeCode.Null]); if (value == null) return BinarySerializationResult.FromImmutable([BinaryTypeCode.Null]);
var runtimeType = value.GetType(); var runtimeType = typeof(T);
var context = AcquireArrayOutputContext(options); var context = AcquireArrayOutputContext(options);
try try
@ -983,7 +940,7 @@ public static partial class AcBinarySerializer
// Only Nullable<T> can be a value type in the Object accessor path. // Only Nullable<T> can be a value type in the Object accessor path.
if (type.IsValueType) if (type.IsValueType)
{ {
if (TryWritePrimitive(value, value.GetType(), context)) if (TryWritePrimitive(value, type, context))
return; return;
} }