Skip to content

Commit

Permalink
refactor(perf): use compiled lambda activator
Browse files Browse the repository at this point in the history
  • Loading branch information
kamranayub committed Jun 13, 2024
1 parent f70ea2d commit 55ad31e
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 9 deletions.
5 changes: 5 additions & 0 deletions IGDB/Identity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,10 @@ public IdentitiesOrValues(object[] values)
var list = values.Select(value => (T)value).ToArray();
Values = list;
}

public IdentitiesOrValues(T[] values)
{
Values = values;
}
}
}
88 changes: 79 additions & 9 deletions IGDB/Serialization/IdentityConverter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static IGDB.Serialization.LambdaActivator;

namespace IGDB
{
Expand Down Expand Up @@ -47,28 +48,33 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
}
}

var valuesActivator = GetValuesActivator(objectType);
var identitiesActivator = GetIdentitiesActivator(objectType);

// If any are objects, it means the IDs should be ignored
if (values.All(v => v.GetType().IsAssignableFrom(typeof(long))))
{
return Activator.CreateInstance(objectType, values.Cast<long>().ToArray());
return identitiesActivator(values.Cast<long>().ToArray());
}

var objects = values.Where(v => !v.GetType().IsAssignableFrom(typeof(long)));
var convertedValues = objects.ToArray();
var ctor = objectType.GetConstructor(new[] { typeof(object[]) });
return ctor.Invoke(new[] { convertedValues });
return valuesActivator(new[] { convertedValues });
}
else if (IsIdentityOrValue(objectType))
{
var identityActivator = GetIdentityActivator(objectType);
var valueActivator = GetValueActivator(objectType);

if (reader.TokenType == JsonToken.StartObject)
{
// objects
return Activator.CreateInstance(objectType, serializer.Deserialize(reader, expandedType));
return valueActivator(serializer.Deserialize(reader, expandedType));
}
else if (reader.TokenType == JsonToken.Integer)
{
// int ids
return Activator.CreateInstance(objectType, (long)reader.Value);
return identityActivator((long)reader.Value);
}
}

Expand Down Expand Up @@ -98,15 +104,79 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s

private static readonly string IdentitiesOrValuesName = typeof(IdentitiesOrValues<>).Name;
private static readonly string IdentityOrValueName = typeof(IdentityOrValue<>).Name;

public static bool IsIdentityOrValue(Type givenType) {

public static bool IsIdentityOrValue(Type givenType)
{
return givenType.Name.Contains(IdentityOrValueName);
}

public static bool IsIdentitiesOrValues(Type givenType) {
public static bool IsIdentitiesOrValues(Type givenType)
{
return givenType.Name.Contains(IdentitiesOrValuesName);
}

private static readonly IDictionary<Type, ObjectActivator> identitiesActivators
= new Dictionary<Type, ObjectActivator>();
private static readonly IDictionary<Type, ObjectActivator> valuesActivators
= new Dictionary<Type, ObjectActivator>();
private static readonly IDictionary<Type, ObjectActivator> identityActivators
= new Dictionary<Type, ObjectActivator>();
private static readonly IDictionary<Type, ObjectActivator> valueActivators
= new Dictionary<Type, ObjectActivator>();

private static ObjectActivator GetIdentitiesActivator(Type objectType)
{
if (identitiesActivators.ContainsKey(objectType))
{
return identitiesActivators[objectType];
}

ConstructorInfo ctor = objectType.GetConstructors().Skip(1).First();
var activator = GetActivator(ctor);
identitiesActivators[objectType] = activator;
return activator;
}

private static ObjectActivator GetValuesActivator(Type objectType)
{
if (valuesActivators.ContainsKey(objectType))
{
return valuesActivators[objectType];
}

ConstructorInfo ctor = objectType.GetConstructors().Skip(2).First();
var activator = GetActivator(ctor);
valuesActivators[objectType] = activator;
return activator;
}

private static ObjectActivator GetIdentityActivator(Type objectType)
{
if (identityActivators.ContainsKey(objectType))
{
return identityActivators[objectType];
}

ConstructorInfo ctor = objectType.GetConstructors().Skip(1).First();
var activator = GetActivator(ctor);
identityActivators[objectType] = activator;
return activator;
}

private static ObjectActivator GetValueActivator(Type objectType)
{
if (valueActivators.ContainsKey(objectType))
{
return valueActivators[objectType];
}

ConstructorInfo ctor = objectType.GetConstructors().Skip(2).First();
var activator = GetActivator(ctor);
valueActivators[objectType] = activator;
return activator;
}
}



}
47 changes: 47 additions & 0 deletions IGDB/Serialization/LamdaActivator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace IGDB.Serialization
{
/// <summary>
/// See: https://rogerjohansson.blog/2008/02/28/linq-expressions-creating-objects/
/// </summary>
public static class LambdaActivator
{
public delegate object ObjectActivator(params object[] args);

public static ObjectActivator GetActivator(ConstructorInfo ctor)
{
ParameterInfo[] paramsInfo = ctor.GetParameters();

ParameterExpression param =
Expression.Parameter(typeof(object[]), "args");

Expression[] argsExp =
new Expression[paramsInfo.Length];

for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;

Expression paramAccessorExp =
Expression.ArrayIndex(param, index);

Expression paramCastExp =
Expression.Convert(paramAccessorExp, paramType);

argsExp[i] = paramCastExp;
}

NewExpression newExp = Expression.New(ctor, argsExp);

LambdaExpression lambda =
Expression.Lambda(typeof(ObjectActivator), newExp, param);

ObjectActivator compiled = (ObjectActivator)lambda.Compile();
return compiled;
}
}
}

0 comments on commit 55ad31e

Please sign in to comment.