using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; #nullable enable using MemoryPack.Internal; using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace MemoryPack { #if NET7_0_OR_GREATER using static MemoryMarshal; using static GC; #else using static MemoryPack.Internal.MemoryMarshalEx; #endif public static partial class MemoryPackSerializer { [ThreadStatic] static SerializerWriterThreadStaticState? threadStaticState; [ThreadStatic] static MemoryPackWriterOptionalState? threadStaticWriterOptionalState; public static byte[] Serialize(in T? value, MemoryPackSerializerOptions? options = default) { if (!RuntimeHelpers.IsReferenceOrContainsReferences()) { var array = AllocateUninitializedArray(Unsafe.SizeOf()); Unsafe.WriteUnaligned(ref GetArrayDataReference(array), value); return array; } #if NET7_0_OR_GREATER var typeKind = TypeHelpers.TryGetUnmanagedSZArrayElementSizeOrMemoryPackableFixedSize(out var elementSize); if (typeKind == TypeHelpers.TypeKind.None) { // do nothing } else if (typeKind == TypeHelpers.TypeKind.UnmanagedSZArray) { if (value == null) { return MemoryPackCode.NullCollectionData.ToArray(); } var srcArray = ((Array)(object)value!); var length = srcArray.Length; if (length == 0) { return new byte[4] { 0, 0, 0, 0 }; } var dataSize = elementSize * length; var destArray = AllocateUninitializedArray(dataSize + 4); ref var head = ref MemoryMarshal.GetArrayDataReference(destArray); Unsafe.WriteUnaligned(ref head, length); Unsafe.CopyBlockUnaligned(ref Unsafe.Add(ref head, 4), ref MemoryMarshal.GetArrayDataReference(srcArray), (uint)dataSize); return destArray; } else if (typeKind == TypeHelpers.TypeKind.FixedSizeMemoryPackable) { var buffer = new byte[(value == null) ? 1 : elementSize]; var bufferWriter = new FixedArrayBufferWriter(buffer); var writer = new MemoryPackWriter(ref bufferWriter, buffer, MemoryPackWriterOptionalState.NullState); Serialize(ref writer, value); return bufferWriter.GetFilledBuffer(); } #endif var state = threadStaticState; if (state == null) { state = threadStaticState = new SerializerWriterThreadStaticState(); } state.Init(options); try { var writer = new MemoryPackWriter(ref Unsafe.As>(ref state.BufferWriter), state.BufferWriter.DangerousGetFirstBuffer(), state.OptionalState); Serialize(ref writer, value); return state.BufferWriter.ToArrayAndReset(); } finally { state.Reset(); } } public static unsafe void Serialize(in IBufferWriter bufferWriter, in T? value, MemoryPackSerializerOptions? options = default) #if NET7_0_OR_GREATER #else #endif { if (!RuntimeHelpers.IsReferenceOrContainsReferences()) { var buffer = bufferWriter.GetSpan(Unsafe.SizeOf()); Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(buffer), value); bufferWriter.Advance(Unsafe.SizeOf()); return; } #if NET7_0_OR_GREATER var typeKind = TypeHelpers.TryGetUnmanagedSZArrayElementSizeOrMemoryPackableFixedSize(out var elementSize); if (typeKind == TypeHelpers.TypeKind.UnmanagedSZArray) { if (value == null) { var span = bufferWriter.GetSpan(4); MemoryPackCode.NullCollectionData.CopyTo(span); bufferWriter.Advance(4); return; } var srcArray = ((Array)(object)value!); var length = srcArray.Length; if (length == 0) { var span = bufferWriter.GetSpan(4); MemoryPackCode.ZeroCollectionData.CopyTo(span); bufferWriter.Advance(4); return; } var dataSize = elementSize * length; var destSpan = bufferWriter.GetSpan(dataSize + 4); ref var head = ref MemoryMarshal.GetReference(destSpan); Unsafe.WriteUnaligned(ref head, length); Unsafe.CopyBlockUnaligned(ref Unsafe.Add(ref head, 4), ref MemoryMarshal.GetArrayDataReference(srcArray), (uint)dataSize); bufferWriter.Advance(dataSize + 4); return; } #endif var state = threadStaticWriterOptionalState; if (state == null) { state = threadStaticWriterOptionalState = new MemoryPackWriterOptionalState(); } state.Init(options); try { var writer = new MemoryPackWriter(ref Unsafe.AsRef(bufferWriter), state); Serialize(ref writer, value); } finally { state.Reset(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Serialize(ref MemoryPackWriter writer, in T? value) #if NET7_0_OR_GREATER #else #endif { writer.WriteValue(value); writer.Flush(); } public static async ValueTask SerializeAsync(Stream stream, T? value, MemoryPackSerializerOptions? options = default, CancellationToken cancellationToken = default) { var tempWriter = ReusableLinkedArrayBufferWriterPool.Rent(); try { Serialize(tempWriter, value, options); await tempWriter.WriteToAndResetAsync(stream, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } finally { ReusableLinkedArrayBufferWriterPool.Return(tempWriter); } } sealed class SerializerWriterThreadStaticState { public ReusableLinkedArrayBufferWriter BufferWriter; public MemoryPackWriterOptionalState OptionalState; public SerializerWriterThreadStaticState() { BufferWriter = new ReusableLinkedArrayBufferWriter(useFirstBuffer: true, pinned: true); OptionalState = new MemoryPackWriterOptionalState(); } public void Init(MemoryPackSerializerOptions? options) { OptionalState.Init(options); } public void Reset() { BufferWriter.Reset(); OptionalState.Reset(); } } } }