public void Serialize(ref MessagePackWriter writer, ref int idx, object value, IFormatterResolver formatterResolver)
        {
            if (value == null)
            {
                writer.WriteNil(ref idx); return;
            }

            var type = value.GetType();

            if (!s_typeNameCache.TryGetValue(type, out Type expectedType))
            {
                GetExpectedTypeSlow(type, out expectedType);
            }

            if (expectedType == null)
            {
                Resolvers.TypelessFormatterFallbackResolver.Instance.GetFormatter <object>().Serialize(ref writer, ref idx, value, formatterResolver);
                return;
            }

            // don't use GetOrAdd for avoid closure capture.
            if (!s_serializers.TryGetValue(expectedType, out var formatterAndDelegate))
            {
                GetFormatterAndDelegateSlow(expectedType, formatterResolver, out formatterAndDelegate);
            }

            // mark as extension with code 100
            writer.Ensure(idx, 6);
            var startOffset = idx;

            idx += 6; // mark will be written at the end, when size is known

            var typeName = MessagePackBinary.GetEncodedTypeName(expectedType);

            UnsafeMemory.WriteRaw(ref writer, typeName, ref idx);
            formatterAndDelegate.Value(formatterAndDelegate.Key, ref writer, ref idx, value, formatterResolver);

            var dataLength = idx - startOffset - 6;

            MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block(ref writer.PinnableAddress, startOffset, ExtensionTypeCode, dataLength);
        }