< Summary

Information
Class: NGql.Core.Caching.TypeCache
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Caching/TypeCache.cs
Line coverage
100%
Covered lines: 30
Uncovered lines: 0
Coverable lines: 30
Total lines: 90
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
GetInternedType(...)100%66100%
InternType(...)100%11100%

File(s)

/home/runner/work/NGql/NGql/src/Core/Caching/TypeCache.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Reflection;
 4using System.Runtime.CompilerServices;
 5
 6namespace NGql.Core.Caching;
 7
 8/// <summary>
 9/// Simple cache for common GraphQL types with memory and CPU optimizations
 10/// </summary>
 11[SuppressMessage("Minor Code Smell", "S3267:Loops should be simplified with \"LINQ\" expressions")]
 12internal static class TypeCache
 13{
 614    private static readonly ConcurrentDictionary<string, string> CustomTypes = new();
 15
 16    // Pre-intern the most common GraphQL types (ordered by frequency)
 617    private static readonly string[] CommonTypes =
 618    [
 619        Constants.DefaultFieldType,    // "String" - most common
 620        "Int", "Boolean", "ID",        // Other common scalars
 621        Constants.ObjectFieldType,     // "object" - for nested fields
 622        "String!", "Int!", "Boolean!", // Non-null variants
 623        "Float", "Float!",             // Less common but still frequent
 624        Constants.ArrayTypeMarker      // "[]" - array marker
 625    ];
 26
 27    // Pre-intern common nullable type patterns
 628    private static readonly string[] CommonNullableTypes =
 629    [
 630        "String?", "Int?", "Boolean?", "ID?", "Float?"
 631    ];
 32
 33    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 34    public static string GetInternedType(ReadOnlySpan<char> type)
 35    {
 36        // ULTRA FAST PATH: Empty type defaults to String
 4674637        if (type.IsEmpty)
 38        {
 639            return Constants.DefaultFieldType;
 40        }
 41
 42        // OPTIMIZED PATH: Single combined check for both common and nullable types
 43        // This eliminates the O(n+m) double-loop pattern and does a single pass
 4674044        var allTypes = CombinedCommonTypes.Value;
 31848945        foreach (var commonType in allTypes)
 46        {
 13540547            if (type.SequenceEqual(commonType.AsSpan()))
 48            {
 4580149                return commonType; // Already interned
 50            }
 51        }
 52
 53        // Standard path for other types
 93954        var typeString = type.ToString();
 93955        return CustomTypes.GetOrAdd(typeString, typeString);
 56    }
 57
 58    // Lazy-initialized combined array to avoid allocation at static init time
 659    private static readonly Lazy<string[]> CombinedCommonTypes = new(() =>
 660    {
 661        var combined = new string[CommonTypes.Length + CommonNullableTypes.Length];
 662        CommonTypes.CopyTo(combined, 0);
 663        CommonNullableTypes.CopyTo(combined, CommonTypes.Length);
 664        return combined;
 665    });
 66
 67    /// <summary>
 68    /// Interns a type string for memory efficiency - alias for GetInternedType
 69    /// </summary>
 70    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 671    public static string InternType(ReadOnlySpan<char> type) => GetInternedType(type);
 72}
 73
 74/// <summary>
 75/// Caches reflection metadata for type introspection
 76/// </summary>
 77internal static class TypeMetadataCache
 78{
 79    /// <summary>
 80    /// Caches PropertyInfo pairs (Key, Value) for KeyValuePair&lt;,&gt; generic types.
 81    /// Caller must guarantee the cached type is a closed KeyValuePair&lt;TKey,TValue&gt; — those
 82    /// always expose Key and Value properties, so the cached pair is non-nullable.
 83    /// </summary>
 84    internal static readonly ConcurrentDictionary<Type, (PropertyInfo Key, PropertyInfo Value)> KvpPropertyCache = new()
 85
 86    /// <summary>
 87    /// Caches PropertyInfo[] per object type for the default WriteObject reflection branch.
 88    /// </summary>
 89    internal static readonly ConcurrentDictionary<Type, PropertyInfo[]> ObjectPropertyCache = new();
 90}