< Summary

Information
Class: NGql.Core.Features.KeyGenerator
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Features/KeyGenerator.cs
Line coverage
100%
Covered lines: 24
Uncovered lines: 0
Coverable lines: 24
Total lines: 72
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GenerateUniqueKey(...)100%11100%
GenerateUniqueKey(...)100%44100%
GenerateUniqueKeyCore(...)100%88100%

File(s)

/home/runner/work/NGql/NGql/src/Core/Features/KeyGenerator.cs

#LineLine coverage
 1using System.Buffers;
 2using NGql.Core.Abstractions;
 3using NGql.Core.Pooling;
 4
 5namespace NGql.Core.Features;
 6
 7/// <summary>
 8/// Utility class for generating unique keys.
 9/// </summary>
 10internal static class KeyGenerator
 11{
 12    /// <summary>
 13    /// Generates a unique key using a <see cref="FieldMergeIndex"/>'s live key set and
 14    /// per-base-name suffix counter instead of rebuilding a <see cref="HashSet{T}"/> from every
 15    /// existing key: O(1) amortized per inserted field across a chain of <c>Include()</c> calls.
 16    /// </summary>
 17    internal static string GenerateUniqueKey(FieldMergeIndex mergeIndex, Dictionary<string, FieldDefinition> fields, str
 91818        => mergeIndex.NextUniqueKey(fields, baseKey);
 19
 20    /// <summary>
 21    /// Generates a unique key from field definitions' effective names (zero-alloc for span iteration).
 22    /// </summary>
 23    internal static string GenerateUniqueKey(string baseKey, ReadOnlySpan<FieldDefinition> fields)
 24    {
 3325        using var pooledSet = LockFreeHashSetPool.GetPooled();
 3326        var existingKeySet = pooledSet.Set;
 27
 28        // Populate set with effective names from span — zero-alloc iteration
 19229        for (int i = 0; i < fields.Length; i++)
 6330            existingKeySet.Add(fields[i]._effectiveName);
 31
 3332        if (!existingKeySet.Contains(baseKey))
 33        {
 634            return baseKey;
 35        }
 36
 2737        return GenerateUniqueKeyCore(baseKey, existingKeySet);
 3338    }
 39
 40    private static string GenerateUniqueKeyCore(string baseKey, HashSet<string> existingKeySet)
 41    {
 42        // 16 chars holds "_" plus a 15-digit counter — counter is int, max ~10 digits.
 2743        var length = checked(baseKey.Length + 16);
 2744        char[]? rented = length > 512 ? ArrayPool<char>.Shared.Rent(length) : null;
 2745        Span<char> buffer = rented is null ? stackalloc char[length] : rented;
 46        try
 47        {
 2748            baseKey.AsSpan().CopyTo(buffer);
 2749            buffer[baseKey.Length] = '_';
 50#if NET9_0_OR_GREATER
 1851            var lookup = existingKeySet.GetAlternateLookup<ReadOnlySpan<char>>();
 52#endif
 53#pragma warning disable S1994
 5154            for (int counter = 1; ; counter++)
 55#pragma warning restore S1994
 56            {
 5157                counter.TryFormat(buffer[(baseKey.Length + 1)..], out var charsWritten);
 5158                var candidate = buffer[..(baseKey.Length + 1 + charsWritten)];
 59#if NET9_0_OR_GREATER
 5260                if (!lookup.Contains(candidate)) return new string(candidate);
 61#else
 1762                var uniqueKey = new string(candidate);
 2663                if (!existingKeySet.Contains(uniqueKey)) return uniqueKey;
 64#endif
 65            }
 66        }
 67        finally
 68        {
 3369            if (rented is not null) ArrayPool<char>.Shared.Return(rented, clearArray: true);
 2770        }
 2771    }
 72}