<commit_before>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Threading;

namespace ExRam.Gremlinq.Core
{
    public interface IQueryFragmentSerializer
    {
        object Serialize<TFragment>(TFragment fragment);

        IQueryFragmentSerializer Override<TFragment>(QueryFragmentSerializer<TFragment> serializer);
    }

    public static class QueryFragmentSerializer
    {
        private sealed class FragmentSerializerImpl : IQueryFragmentSerializer
        {
            private readonly IImmutableDictionary<Type, Delegate> _dict;
            private ConcurrentDictionary<(Type staticType, Type actualType), Delegate?>? _fastDict;

            public FragmentSerializerImpl(IImmutableDictionary<Type, Delegate> dict)
            {
                _dict = dict;
            }

            public object Serialize<TFragment>(TFragment fragment)
            {
                return TryGetSerializer(typeof(TFragment), fragment.GetType()) is QueryFragmentSerializer<TFragment> del
                    ? del(fragment, _ => _, this)
                    : fragment;
            }

            public IQueryFragmentSerializer Override<TFragment>(QueryFragmentSerializer<TFragment> serializer)
            {
                return new FragmentSerializerImpl(
                    _dict.SetItem(
                        typeof(TFragment),
                        _dict.TryGetValue(typeof(TFragment), out var existingAtomSerializer)
                            ? new QueryFragmentSerializer<TFragment>((atom, baseSerializer, recurse) => serializer(atom, _ => ((QueryFragmentSerializer<TFragment>)existingAtomSerializer)(_!, baseSerializer, recurse), recurse))
                            : (atom, baseSerializer, recurse) => serializer(atom, _ => baseSerializer(_!), recurse)));
            }

            private Delegate? TryGetSerializer(Type staticType, Type actualType)
            {
                var fastDict = Volatile.Read(ref _fastDict);
                if (fastDict == null)
                    Volatile.Write(ref _fastDict, fastDict = new ConcurrentDictionary<(Type, Type), Delegate?>());

                return fastDict
                    .GetOrAdd(
                        (staticType, actualType),
                        (typeTuple, @this) =>
                        {
                            Delegate InnerLookup(Type staticType, Type actualType)
                            {
                                if (@this._dict.TryGetValue(actualType, out var ret))
                                    return ret;

                                foreach (var implementedInterface in actualType.GetInterfaces())
                                {
                                    if (InnerLookup(staticType, implementedInterface) is { } interfaceSerializer)
                                        return interfaceSerializer;
                                }

                                if (actualType.BaseType is { } baseType)
                                {
                                    if (InnerLookup(staticType, baseType) is { } baseSerializer)
                                        return baseSerializer;
                                }

                                return null;
                            }

                            if (InnerLookup(typeTuple.staticType, typeTuple.actualType) is { } del)
                            {
                                var effectiveType = del.GetType().GetGenericArguments()[0];

                                if (effectiveType == typeTuple.staticType)
                                    return del;

                                //Func<TStatic, object>
                                var staticBaseSerializerType = typeof(Func<,>).MakeGenericType(staticType, typeof(object));

                                //Func<TActual, object>
                                var actualBaseSerializerType = typeof(Func<,>).MakeGenericType(effectiveType, typeof(object));

                                //return (TStatic fragment, Func<TStatic, object> baseSerializer, IQueryFragmentSerializer recurse) => ret((TActualType)fragment, (TActual _) => baseSerializer((TStatic)_), recurse);

                                var staticQuerySerializerType = typeof(QueryFragmentSerializer<>).MakeGenericType(staticType);

                                var fragmentParameterExpression = Expression.Parameter(staticType);
                                var baseSerializerParameterExpression = Expression.Parameter(staticBaseSerializerType);
                                var recurseParameter = Expression.Parameter(typeof(IQueryFragmentSerializer));

                                //(TActualType)fragment
                                var argument1 = Expression.Convert(fragmentParameterExpression, effectiveType);

                                //(TActual _) => baseSerializer((TFragment)_)
                                var argument2Parameter = Expression.Parameter(effectiveType);
                                var argument2 = Expression.Lambda(
                                    actualBaseSerializerType,
                                    Expression.Invoke(
                                        baseSerializerParameterExpression,
                                        Expression.Convert(argument2Parameter, staticType)),
                                    argument2Parameter);

                                var retCall = Expression.Invoke(Expression.Constant(del), argument1, argument2, recurseParameter);

                                return Expression
                                    .Lambda(staticQuerySerializerType, retCall, fragmentParameterExpression, baseSerializerParameterExpression, recurseParameter)
                                    .Compile();
                            }

                            return null;
                        },
                        this);
            }
        }

        public static readonly IQueryFragmentSerializer Identity = new FragmentSerializerImpl(ImmutableDictionary<Type, Delegate>.Empty);
    }

    public delegate object QueryFragmentSerializer<TFragment>(TFragment fragment, Func<TFragment, object> baseSerializer, IQueryFragmentSerializer recurse);
}<commit_msg>Return a Func in QueryFragmentSerializer.TryGetSerializer.<commit_after>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Threading;

namespace ExRam.Gremlinq.Core
{
    public interface IQueryFragmentSerializer
    {
        object Serialize<TFragment>(TFragment fragment);

        IQueryFragmentSerializer Override<TFragment>(QueryFragmentSerializer<TFragment> serializer);
    }

    public static class QueryFragmentSerializer
    {
        private sealed class FragmentSerializerImpl : IQueryFragmentSerializer
        {
            private readonly IImmutableDictionary<Type, Delegate> _dict;
            private ConcurrentDictionary<(Type staticType, Type actualType), Delegate?>? _fastDict;

            public FragmentSerializerImpl(IImmutableDictionary<Type, Delegate> dict)
            {
                _dict = dict;
            }

            public object Serialize<TFragment>(TFragment fragment)
            {
                return TryGetSerializer(typeof(TFragment), fragment.GetType()) is Func<TFragment, object> del
                    ? del(fragment)
                    : fragment;
            }

            public IQueryFragmentSerializer Override<TFragment>(QueryFragmentSerializer<TFragment> serializer)
            {
                return new FragmentSerializerImpl(
                    _dict.SetItem(
                        typeof(TFragment),
                        _dict.TryGetValue(typeof(TFragment), out var existingAtomSerializer)
                            ? new QueryFragmentSerializer<TFragment>((atom, baseSerializer, recurse) => serializer(atom, _ => ((QueryFragmentSerializer<TFragment>)existingAtomSerializer)(_!, baseSerializer, recurse), recurse))
                            : (atom, baseSerializer, recurse) => serializer(atom, _ => baseSerializer(_!), recurse)));
            }

            private Delegate? TryGetSerializer(Type staticType, Type actualType)
            {
                var fastDict = Volatile.Read(ref _fastDict);
                if (fastDict == null)
                    Volatile.Write(ref _fastDict, fastDict = new ConcurrentDictionary<(Type, Type), Delegate?>());

                return fastDict
                    .GetOrAdd(
                        (staticType, actualType),
                        (typeTuple, @this) =>
                        {
                            Delegate? InnerLookup(Type actualType)
                            {
                                if (@this._dict.TryGetValue(actualType, out var ret))
                                    return ret;

                                foreach (var implementedInterface in actualType.GetInterfaces())
                                {
                                    if (InnerLookup(implementedInterface) is { } interfaceSerializer)
                                        return interfaceSerializer;
                                }

                                if (actualType.BaseType is { } baseType)
                                {
                                    if (InnerLookup(baseType) is { } baseSerializer)
                                        return baseSerializer;
                                }

                                return null;
                            }

                            if (InnerLookup(typeTuple.actualType) is { } del)
                            {
                                //return (TStatic fragment) => del((TActualType)fragment, (TActual _) => _, @this);

                                var effectiveType = del.GetType().GetGenericArguments()[0];

                                var argument2Parameter = Expression.Parameter(effectiveType);
                                var fragmentParameterExpression = Expression.Parameter(staticType);

                                var retCall = Expression.Invoke(
                                    Expression.Constant(del),
                                    Expression.Convert(
                                        fragmentParameterExpression,
                                        effectiveType),
                                    Expression.Lambda(
                                        typeof(Func<,>).MakeGenericType(effectiveType, typeof(object)), //Func<TActual, object>
                                        Expression.Convert(argument2Parameter, typeof(object)),
                                        argument2Parameter),
                                    Expression.Constant(@this));

                                return Expression
                                    .Lambda(
                                        typeof(Func<,>)
                                            .MakeGenericType(staticType, typeof(object)),
                                        retCall,
                                        fragmentParameterExpression)
                                    .Compile();
                            }

                            return null;
                        },
                        this);
            }
        }

        public static readonly IQueryFragmentSerializer Identity = new FragmentSerializerImpl(ImmutableDictionary<Type, Delegate>.Empty);
    }

    public delegate object QueryFragmentSerializer<TFragment>(TFragment fragment, Func<TFragment, object> baseSerializer, IQueryFragmentSerializer recurse);
}
{'fraction_non_alphanumeric': 0.06920268319606011, 'fraction_numerical': 0.0010189352127027256, 'mean_word_length': 1.360320641282565, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 56, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7589601', 'n_tokens_mistral': 2420, 'n_tokens_neox': 2247, 'n_words': 474}