Skip to content

Commit

Permalink
Add a shared caching infrastructure for PolyType applications. (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis authored Nov 23, 2024
1 parent fa1a51a commit 085488b
Show file tree
Hide file tree
Showing 41 changed files with 2,062 additions and 1,240 deletions.
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,9 @@ dotnet_diagnostic.xUnit2005.severity = none

# SA1208: System using directives should be placed before other using directives
dotnet_diagnostic.SA1208.severity = none

# IDE0021: Use block body for constructor
dotnet_diagnostic.IDE0021.severity = none

# IDE0032: Use auto property
dotnet_diagnostic.IDE0032.severity = none
25 changes: 14 additions & 11 deletions src/PolyType.Examples/CborSerializer/CborSerializer.Builder.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
using PolyType.Abstractions;
using PolyType.Examples.CborSerializer.Converters;
using PolyType.Examples.Utilities;
using PolyType.Utilities;

namespace PolyType.Examples.CborSerializer;

public static partial class CborSerializer
{
private sealed class Builder : ITypeShapeVisitor
private sealed class Builder(TypeGenerationContext generationContext) : ITypeShapeVisitor, ITypeShapeFunc
{
private readonly TypeDictionary _cache = new();
public CborConverter<T> GetOrAddConverter<T>(ITypeShape<T> typeShape) =>
(CborConverter<T>)generationContext.GetOrAdd(typeShape, this)!;

public CborConverter<T> BuildConverter<T>(ITypeShape<T> typeShape)
object? ITypeShapeFunc.Invoke<T>(ITypeShape<T> typeShape, object? shape)
{
// Check if the type has a built-in converter.
if (s_builtInConverters.TryGetValue(typeof(T), out CborConverter? defaultConverter))
{
return (CborConverter<T>)defaultConverter;
}

return _cache.GetOrAdd<CborConverter<T>>(typeShape, this, self => new DelayedCborConverter<T>(self));
// Otherwise, build a converter using the visitor.
return typeShape.Accept(this);
}

public object? VisitObject<T>(IObjectTypeShape<T> objectShape, object? state)
Expand All @@ -35,7 +38,7 @@ public CborConverter<T> BuildConverter<T>(ITypeShape<T> typeShape)

public object? VisitProperty<TDeclaringType, TPropertyType>(IPropertyShape<TDeclaringType, TPropertyType> property, object? state)
{
CborConverter<TPropertyType> propertyConverter = BuildConverter(property.PropertyType);
CborConverter<TPropertyType> propertyConverter = GetOrAddConverter(property.PropertyType);
return new CborPropertyConverter<TDeclaringType, TPropertyType>(property, propertyConverter);
}

Expand All @@ -62,13 +65,13 @@ public CborConverter<T> BuildConverter<T>(ITypeShape<T> typeShape)

public object? VisitConstructorParameter<TArgumentState, TParameterType>(IConstructorParameterShape<TArgumentState, TParameterType> parameter, object? state)
{
CborConverter<TParameterType> paramConverter = BuildConverter(parameter.ParameterType);
CborConverter<TParameterType> paramConverter = GetOrAddConverter(parameter.ParameterType);
return new CborPropertyConverter<TArgumentState, TParameterType>(parameter, paramConverter);
}

public object? VisitEnumerable<TEnumerable, TElement>(IEnumerableTypeShape<TEnumerable, TElement> enumerableShape, object? state)
{
CborConverter<TElement> elementConverter = BuildConverter(enumerableShape.ElementType);
CborConverter<TElement> elementConverter = GetOrAddConverter(enumerableShape.ElementType);
Func<TEnumerable, IEnumerable<TElement>> getEnumerable = enumerableShape.GetGetEnumerable();

return enumerableShape.ConstructionStrategy switch
Expand Down Expand Up @@ -98,8 +101,8 @@ public CborConverter<T> BuildConverter<T>(ITypeShape<T> typeShape)

public object? VisitDictionary<TDictionary, TKey, TValue>(IDictionaryTypeShape<TDictionary, TKey, TValue> dictionaryShape, object? state) where TKey : notnull
{
CborConverter<TKey> keyConverter = BuildConverter(dictionaryShape.KeyType);
CborConverter<TValue> valueConverter = BuildConverter(dictionaryShape.ValueType);
CborConverter<TKey> keyConverter = GetOrAddConverter(dictionaryShape.KeyType);
CborConverter<TValue> valueConverter = GetOrAddConverter(dictionaryShape.ValueType);
Func<TDictionary, IReadOnlyDictionary<TKey, TValue>> getDictionary = dictionaryShape.GetGetDictionary();

return dictionaryShape.ConstructionStrategy switch
Expand Down Expand Up @@ -132,7 +135,7 @@ public CborConverter<T> BuildConverter<T>(ITypeShape<T> typeShape)

public object? VisitNullable<T>(INullableTypeShape<T> nullableShape, object? state) where T : struct
{
CborConverter<T> elementConverter = BuildConverter(nullableShape.ElementType);
CborConverter<T> elementConverter = GetOrAddConverter(nullableShape.ElementType);
return new CborNullableConverter<T>(elementConverter);
}

Expand Down
26 changes: 23 additions & 3 deletions src/PolyType.Examples/CborSerializer/CborSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System.Diagnostics;
using PolyType.Abstractions;
using PolyType.Examples.CborSerializer.Converters;
using PolyType.Utilities;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Formats.Cbor;
using PolyType.Abstractions;
using System.Text.Json.Serialization;

namespace PolyType.Examples.CborSerializer;

Expand All @@ -9,14 +13,20 @@ namespace PolyType.Examples.CborSerializer;
/// </summary>
public static partial class CborSerializer
{
private static readonly MultiProviderTypeCache s_converterCaches = new()
{
DelayedValueFactory = new DelayedCborConverterFactory(),
ValueBuilderFactory = ctx => new Builder(ctx),
};

/// <summary>
/// Builds an <see cref="CborConverter{T}"/> instance from the specified shape.
/// </summary>
/// <typeparam name="T">The type for which to build the converter.</typeparam>
/// <param name="shape">The shape instance guiding converter construction.</param>
/// <returns>An <see cref="CborConverter{T}"/> instance.</returns>
public static CborConverter<T> CreateConverter<T>(ITypeShape<T> shape) =>
new Builder().BuildConverter(shape);
(CborConverter<T>)s_converterCaches.GetOrAdd(shape)!;

/// <summary>
/// Builds an <see cref="CborConverter{T}"/> instance from the specified shape provider.
Expand All @@ -27,6 +37,16 @@ public static CborConverter<T> CreateConverter<T>(ITypeShape<T> shape) =>
public static CborConverter<T> CreateConverter<T>(ITypeShapeProvider shapeProvider) =>
CreateConverter(shapeProvider.Resolve<T>());

/// <summary>
/// Builds a <see cref="JsonConverter"/> instance from the reflection-based shape provider.
/// </summary>
/// <typeparam name="T">The type for which to build the converter.</typeparam>
/// <returns>An <see cref="JsonConverter{T}"/> instance.</returns>
[RequiresUnreferencedCode("PolyType reflection provider requires unreferenced code")]
[RequiresDynamicCode("PolyType reflection provider requires dynamic code")]
public static CborConverter<T> CreateConverterUsingReflection<T>() =>
CreateConverter<T>(ReflectionProvider.ReflectionTypeShapeProvider.Default);

/// <summary>
/// Serializes a value to a CBOR encoding using the provided converter.
/// </summary>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using PolyType.Abstractions;
using PolyType.Utilities;
using System.Formats.Cbor;

namespace PolyType.Examples.CborSerializer.Converters;

internal sealed class DelayedCborConverterFactory : IDelayedValueFactory
{
public DelayedValue Create<T>(ITypeShape<T> _) => new DelayedValue<CborConverter<T>>(self => new DelayedCborConverter<T>(self));

private sealed class DelayedCborConverter<T>(DelayedValue<CborConverter<T>> self) : CborConverter<T>
{
public override T? Read(CborReader reader)
=> self.Result.Read(reader);

public override void Write(CborWriter writer, T? value)
=> self.Result.Write(writer, value);
}
}
Loading

0 comments on commit 085488b

Please sign in to comment.