< Summary

Information
Class: NGql.Core.Extensions.SpanExtensions
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Extensions/SpanExtensions.cs
Line coverage
100%
Covered lines: 80
Uncovered lines: 0
Coverable lines: 80
Total lines: 233
Line coverage: 100%
Branch coverage
100%
Covered branches: 60
Total branches: 60
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
TryGetValue(...)100%66100%
SetValue(...)100%11100%
GetOrAddSimpleField(...)100%66100%
GetOrAddSimpleField(...)100%66100%
MergeArgumentsAndMetadata(...)100%88100%
AppendNew(...)100%11100%
AppendNewWithParentPath(...)100%22100%
StoreNew(...)100%11100%
StoreNewWithParentPath(...)100%22100%
ClassifyFieldFast(...)100%1010100%
IsSimpleField(...)100%44100%
IsDottedField(...)100%44100%
HasLetterOrDigit(...)100%44100%
EqualsIgnoreCase(...)100%11100%
TrimEndDotsAndSpaces(...)100%66100%
ExtractFieldName(...)100%22100%

File(s)

/home/runner/work/NGql/NGql/src/Core/Extensions/SpanExtensions.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Runtime.CompilerServices;
 3using NGql.Core.Abstractions;
 4using NGql.Core.Builders;
 5
 6namespace NGql.Core.Extensions;
 7
 8[SuppressMessage("Minor Code Smell", "S3267:Loops should be simplified with \"LINQ\" expressions")]
 9internal static class SpanExtensions
 10{
 11    /// <summary>
 12    /// Try to get value from a dictionary using a span key without allocating string
 13    /// </summary>
 14    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 15    public static bool TryGetValue(this Dictionary<string, FieldDefinition> dictionary, ReadOnlySpan<char> key, out Fiel
 16    {
 845117        value = null;
 18
 19        // Fast path: check if any key matches the span length first to avoid unnecessary comparisons
 845120        var keyLength = key.Length;
 1788321        foreach (var kvp in dictionary)
 22        {
 86423            if (kvp.Key.Length != keyLength || !kvp.Key.AsSpan().SequenceEqual(key))
 24            {
 25                continue;
 26            }
 27
 74728            value = kvp.Value;
 74729            return true;
 30        }
 31
 770432        return false;
 74733    }
 34
 35    /// <summary>
 36    /// Set value in a dictionary using a span key, converting to string only when needed
 37    /// </summary>
 38    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 39    public static void SetValue(this Dictionary<string, FieldDefinition> dictionary, ReadOnlySpan<char> key, FieldDefini
 40    {
 809441        var keyString = key.ToString();
 809442        dictionary[keyString] = value;
 809443    }
 44
 45    /// <summary>
 46    /// Get or add a simple field using a span key with optimized path building — FieldChildren variant
 47    /// </summary>
 48    public static FieldDefinition GetOrAddSimpleField(this FieldChildren children, ReadOnlySpan<char> fieldName, ReadOnl
 49    {
 219050        if (children.TryGetValue(fieldName, out var existingField) && existingField is not null)
 51        {
 2152            existingField = MergeArgumentsAndMetadata(existingField, arguments, metadata);
 2153            children.Set(fieldName, existingField);
 2154            return existingField;
 55        }
 56
 216957        return string.IsNullOrWhiteSpace(parentPath)
 216958            ? AppendNew(children, fieldName, fieldType, arguments, fieldName, metadata)
 216959            : AppendNewWithParentPath(children, fieldName, fieldType, arguments, parentPath, metadata);
 60    }
 61
 62    /// <summary>
 63    /// Get or add a simple field using a span key with optimized path building
 64    /// </summary>
 65    internal static FieldDefinition GetOrAddSimpleField(this Dictionary<string, FieldDefinition> fieldDefinitions, ReadO
 66    {
 762067        if (fieldDefinitions.TryGetValue(fieldName, out var existingField) && existingField is not null)
 68        {
 8469            existingField = MergeArgumentsAndMetadata(existingField, arguments, metadata);
 8470            fieldDefinitions.SetValue(fieldName, existingField);
 8471            return existingField;
 72        }
 73
 753674        return string.IsNullOrWhiteSpace(parentPath)
 753675            ? StoreNew(fieldDefinitions, fieldName, fieldType, arguments, fieldName, metadata)
 753676            : StoreNewWithParentPath(fieldDefinitions, fieldName, fieldType, arguments, parentPath, metadata);
 77    }
 78
 79    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 80    private static FieldDefinition MergeArgumentsAndMetadata(FieldDefinition existing, IDictionary<string, object?>? arg
 81    {
 10582        if (arguments is { Count: > 0 })
 83        {
 2784            existing = existing.MergeFieldArguments(arguments);
 85        }
 10586        if (metadata is { Count: > 0 })
 87        {
 688            var mergedMetadata = Helpers.MergeNullableMetadata(existing._metadata, metadata);
 689            existing = existing with { Metadata = mergedMetadata };
 90        }
 10591        return existing;
 92    }
 93
 94    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 95    private static FieldDefinition AppendNew(FieldChildren children, ReadOnlySpan<char> fieldName, ReadOnlySpan<char> fi
 96    {
 216997        var field = Helpers.CreateFieldDefinition(fieldName, fieldType, ReadOnlySpan<char>.Empty, arguments, path, metad
 216998        children.Append(field);
 216999        return field;
 100    }
 101
 102    private static FieldDefinition AppendNewWithParentPath(FieldChildren children, ReadOnlySpan<char> fieldName, ReadOnl
 103    {
 2094104        var estimatedLength = parentPath.Length + 1 + fieldName.Length;
 2094105        if (estimatedLength <= 256)
 106        {
 2076107            Span<char> pathBuffer = stackalloc char[estimatedLength];
 2076108            var pathBuilder = new SpanPathBuilder(pathBuffer);
 2076109            pathBuilder.Append(parentPath.AsSpan());
 2076110            pathBuilder.Append(fieldName);
 2076111            return AppendNew(children, fieldName, fieldType, arguments, pathBuilder.AsSpan(), metadata);
 112        }
 113
 18114        var fieldPath = $"{parentPath}.{fieldName}";
 18115        return AppendNew(children, fieldName, fieldType, arguments, fieldPath.AsSpan(), metadata);
 116    }
 117
 118    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 119    private static FieldDefinition StoreNew(Dictionary<string, FieldDefinition> fieldDefinitions, ReadOnlySpan<char> fie
 120    {
 7536121        var field = Helpers.CreateFieldDefinition(fieldName, fieldType, ReadOnlySpan<char>.Empty, arguments, path, metad
 7536122        fieldDefinitions.SetValue(fieldName, field);
 7536123        return field;
 124    }
 125
 126    private static FieldDefinition StoreNewWithParentPath(Dictionary<string, FieldDefinition> fieldDefinitions, ReadOnly
 127    {
 24128        var estimatedLength = parentPath.Length + 1 + fieldName.Length;
 24129        if (estimatedLength <= 256)
 130        {
 6131            Span<char> pathBuffer = stackalloc char[estimatedLength];
 6132            var pathBuilder = new SpanPathBuilder(pathBuffer);
 6133            pathBuilder.Append(parentPath.AsSpan());
 6134            pathBuilder.Append(fieldName);
 6135            return StoreNew(fieldDefinitions, fieldName, fieldType, arguments, pathBuilder.AsSpan(), metadata);
 136        }
 137
 18138        var fieldPath = $"{parentPath}.{fieldName}";
 18139        return StoreNew(fieldDefinitions, fieldName, fieldType, arguments, fieldPath.AsSpan(), metadata);
 140    }
 141
 142    /// <summary>
 143    /// Vectorized field classification - checks all conditions in one pass
 144    /// </summary>
 145    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 146    public static (bool HasSpaces, bool HasDots, bool HasColons) ClassifyFieldFast(this ReadOnlySpan<char> span)
 147    {
 190773148        bool hasSpaces = false, hasDots = false, hasColons = false;
 149
 150        // ULTRA FAST PATH: Single pass through the span
 2292369151        foreach (var c in span)
 152        {
 153            switch (c)
 154            {
 155                case ' ':
 921156                    hasSpaces = true;
 921157                    break;
 158                case '.':
 106092159                    hasDots = true;
 106092160                    break;
 161                case ':':
 3963162                    hasColons = true;
 163                    break;
 164            }
 165
 166            // Early exit if all conditions found
 1082610167            if (hasSpaces && hasDots && hasColons) break;
 168        }
 169
 63591170        return (hasSpaces, hasDots, hasColons);
 171    }
 172
 173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 174    public static bool IsSimpleField(this ReadOnlySpan<char> span)
 175    {
 45933176        var (hasSpaces, hasDots, hasColons) = span.ClassifyFieldFast();
 45933177        return !hasSpaces && !hasDots && !hasColons;
 178    }
 179
 180    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 181    public static bool IsDottedField(this ReadOnlySpan<char> span)
 182    {
 17637183        var (hasSpaces, hasDots, hasColons) = span.ClassifyFieldFast();
 17637184        return hasDots && !hasSpaces && !hasColons;
 185    }
 186
 187    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 188    public static bool HasLetterOrDigit(this ReadOnlySpan<char> span)
 189    {
 1605190        foreach (var c in span)
 191        {
 546192            if (char.IsLetterOrDigit(c))
 193            {
 381194                return true;
 195            }
 196        }
 66197        return false;
 198    }
 199
 200    /// <summary>
 201    /// Efficient case-insensitive comparison for spans
 202    /// </summary>
 203    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 204    public static bool EqualsIgnoreCase(this ReadOnlySpan<char> span, ReadOnlySpan<char> other)
 205    {
 15206        return span.Equals(other, StringComparison.OrdinalIgnoreCase);
 207    }
 208
 209    /// <summary>
 210    /// Trim whitespace and dots from span efficiently
 211    /// </summary>
 212    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 213    public static ReadOnlySpan<char> TrimEndDotsAndSpaces(this ReadOnlySpan<char> span)
 214    {
 4923215        var end = span.Length - 1;
 5016216        while (end >= 0 && (span[end] == '.' || char.IsWhiteSpace(span[end])))
 217        {
 93218            end--;
 219        }
 4923220        return span[..(end + 1)];
 221    }
 222
 223    /// <summary>
 224    /// Extract the field name from a segment by taking everything after the last colon
 225    /// </summary>
 226    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 227    public static ReadOnlySpan<char> ExtractFieldName(this ReadOnlySpan<char> segment)
 228    {
 15229        var colonIndex = segment.LastIndexOf(':');
 15230        var fieldName = colonIndex == -1 ? segment : segment[(colonIndex + 1)..];
 15231        return fieldName.Trim();
 232    }
 233}

Methods/Properties

TryGetValue(System.Collections.Generic.Dictionary`2<System.String,NGql.Core.Abstractions.FieldDefinition>,System.ReadOnlySpan`1<System.Char>,NGql.Core.Abstractions.FieldDefinition&)
SetValue(System.Collections.Generic.Dictionary`2<System.String,NGql.Core.Abstractions.FieldDefinition>,System.ReadOnlySpan`1<System.Char>,NGql.Core.Abstractions.FieldDefinition)
GetOrAddSimpleField(NGql.Core.Abstractions.FieldChildren,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
GetOrAddSimpleField(System.Collections.Generic.Dictionary`2<System.String,NGql.Core.Abstractions.FieldDefinition>,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
MergeArgumentsAndMetadata(NGql.Core.Abstractions.FieldDefinition,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
AppendNew(NGql.Core.Abstractions.FieldChildren,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
AppendNewWithParentPath(NGql.Core.Abstractions.FieldChildren,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
StoreNew(System.Collections.Generic.Dictionary`2<System.String,NGql.Core.Abstractions.FieldDefinition>,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
StoreNewWithParentPath(System.Collections.Generic.Dictionary`2<System.String,NGql.Core.Abstractions.FieldDefinition>,System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>)
ClassifyFieldFast(System.ReadOnlySpan`1<System.Char>)
IsSimpleField(System.ReadOnlySpan`1<System.Char>)
IsDottedField(System.ReadOnlySpan`1<System.Char>)
HasLetterOrDigit(System.ReadOnlySpan`1<System.Char>)
EqualsIgnoreCase(System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>)
TrimEndDotsAndSpaces(System.ReadOnlySpan`1<System.Char>)
ExtractFieldName(System.ReadOnlySpan`1<System.Char>)