< Summary

Information
Class: NGql.Core.Abstractions.FieldChildren
Assembly: NGql.Core
File(s): /home/runner/work/NGql/NGql/src/Core/Abstractions/FieldChildren.cs
Line coverage
100%
Covered lines: 137
Uncovered lines: 0
Coverable lines: 137
Total lines: 475
Line coverage: 100%
Branch coverage
100%
Covered branches: 70
Total branches: 70
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
.ctor(...)100%11100%
System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<System.String,NGql.Core.Abstractions.FieldDefinition>>.get_Count()100%11100%
get_Count()100%11100%
AsSpan()100%22100%
Find(...)100%1010100%
Find(...)100%1010100%
TryGetValue(...)100%11100%
TryGetValue(...)100%11100%
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.TryGetValue(...)100%11100%
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.ContainsKey(...)100%11100%
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Item(...)100%22100%
Append(...)100%11100%
Set(...)100%1010100%
Set(...)100%44100%
TryGetIndexSlot(...)100%11100%
TryGetIndexSlot(...)100%11100%
RemoveIndexKey(...)100%11100%
RemoveIndexKey(...)100%11100%
UpdateIndexKeyLocked(...)100%22100%
ReplaceReference(...)100%1212100%
Add(...)100%11100%
AppendLocked(...)100%88100%
BuildIndexLocked()100%22100%
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Keys()100%44100%
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Values()100%44100%
GetEnumerator()100%11100%
System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.String,NGql.Core.Abstractions.FieldDefinition>>.GetEnumerator()100%11100%
System.Collections.IEnumerable.GetEnumerator()100%11100%

File(s)

/home/runner/work/NGql/NGql/src/Core/Abstractions/FieldChildren.cs

#LineLine coverage
 1using System.Collections;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Threading;
 4
 5namespace NGql.Core.Abstractions;
 6
 7/// <summary>
 8/// Array-backed ordered collection of child <see cref="FieldDefinition"/> instances.
 9/// Uses linear scan for small counts and a lazy dictionary index for larger counts.
 10/// Null <see cref="FieldDefinition._children"/> on a leaf node costs nothing — no allocation occurs
 11/// until the first child is added.
 12///
 13/// THREAD-SAFETY: Reads on small (no-index) collections are lock-free — readers do volatile loads
 14/// of <see cref="_count"/> and <see cref="_items"/> and walk a contiguous array. Reads against the
 15/// index, and all writes, take <see cref="_lock"/>. Writers publish updates with explicit volatile
 16/// stores: the new slot is written before <see cref="_count"/> bumps, so any reader observing the
 17/// new count is also guaranteed to see the corresponding initialized slot.
 18///
 19/// The index threshold is intentionally moderate: tiny collections do not need an index, and avoiding
 20/// it on the read fast-path eliminates lock acquisition for the common case (most query nodes have
 21/// fewer than a dozen direct children).
 22/// </summary>
 23internal sealed class FieldChildren : IReadOnlyDictionary<string, FieldDefinition>
 24{
 25    private const int InitialCapacity = 4;
 26    private const int IndexThreshold = 16;
 27
 14645728    internal FieldChildren()
 29    {
 14645730    }
 31
 32    /// <summary>
 33    /// Creates the collection pre-sized for a known child count, avoiding the grow-and-copy
 34    /// cycles of the default 4-slot start. Used by clone paths where the count is known upfront.
 35    /// </summary>
 394536    internal FieldChildren(int capacity)
 394537        => _items = new FieldDefinition[Math.Max(capacity, InitialCapacity)];
 38
 39    /// <summary>Backing storage. Written only under <see cref="_lock"/>; readers do a single volatile load.</summary>
 40    private FieldDefinition[]? _items;
 41    /// <summary>Number of valid entries in <see cref="_items"/>. Volatile-stored last on writes
 42    /// (release semantics) so readers observing the new value also see the corresponding slot.</summary>
 43    private int _count;
 44    /// <summary>Lazy lookup index, built when <see cref="_count"/> reaches <see cref="IndexThreshold"/>.
 45    /// Maps name to its SLOT INDEX in <see cref="_items"/> (not the <see cref="FieldDefinition"/> itself),
 46    /// so a hit gives both the current value (via <c>_items[slot]</c>) and the position needed for an
 47    /// in-place replace. All access (including reads) is guarded by <see cref="_lock"/> because
 48    /// <see cref="Dictionary{TKey,TValue}"/> is not safe for concurrent read+write.
 49    /// <para>
 50    /// <b>"Last occurrence wins" is the intended invariant for <see cref="AppendLocked"/> and
 51    /// <see cref="BuildIndexLocked"/>, but <see cref="ReplaceReference"/>'s linear-scan fallback
 52    /// (reached only for the rare same-name/different-alias collision the index cannot fully
 53    /// disambiguate) can re-point a name's slot at an EARLIER same-named sibling's position via
 54    /// <see cref="UpdateIndexKeyLocked"/>, technically breaking that invariant for the remainder of
 55    /// that name's entries.</b> No observable misbehavior is currently known: every caller that
 56    /// legitimately needs to distinguish same-named/different-alias siblings
 57    /// (<c>NGql.Core.Extensions.Helpers.FindExistingField(FieldChildren, FieldDefinition)</c>,
 58    /// <c>NGql.Core.Extensions.FieldDefinitionExtensions.FindChildByNameAndAlias</c>) already falls back to an exhausti
 59    /// scan rather than trusting a bare name-index hit as authoritative. Documented here rather than
 60    /// changed because a fix would need to touch the same re-bucketing/index machinery under active,
 61    /// separate revision — flag before altering.
 62    /// </para></summary>
 63    private Dictionary<string, int>? _index;
 15040264    private readonly object _lock = new();
 65
 66    // ── Counts ────────────────────────────────────────────────────────────────
 67
 1568    int IReadOnlyCollection<KeyValuePair<string, FieldDefinition>>.Count => Volatile.Read(ref _count);
 56693769    internal int Count => Volatile.Read(ref _count);
 70
 71    // ── Span access ───────────────────────────────────────────────────────────
 72
 73    /// <summary>Returns a span over the items for zero-alloc iteration.
 74    /// Reads count first, then items — combined with the writer's items-then-count publication this
 75    /// guarantees the observed array contains every slot below the observed count.</summary>
 76    internal ReadOnlySpan<FieldDefinition> AsSpan()
 77    {
 31431678        var count = Volatile.Read(ref _count);
 31431679        var items = Volatile.Read(ref _items);
 31431680        return items == null ? ReadOnlySpan<FieldDefinition>.Empty : items.AsSpan(0, count);
 81    }
 82
 83    // ── Lookup ────────────────────────────────────────────────────────────────
 84
 85    /// <summary>Find a child by name (case-insensitive). Returns null if not found.</summary>
 86    internal FieldDefinition? Find(ReadOnlySpan<char> name)
 87    {
 88        // Fast path: no index yet. Lock-free volatile snapshot + linear scan.
 16744289        if (Volatile.Read(ref _index) == null)
 90        {
 16357191            var count = Volatile.Read(ref _count);
 16357192            var items = Volatile.Read(ref _items);
 31208293            if (items == null) return null;
 5862694            for (int i = 0; i < count; i++)
 95            {
 2041296                if (name.Equals(items[i].Name.AsSpan(), StringComparison.OrdinalIgnoreCase))
 615997                    return items[i];
 98            }
 890199            return null;
 100        }
 101
 102        // Slow path: indexed lookup needs the lock since Dictionary is not concurrent-read-safe.
 103        // _index is monotone: once published non-null by BuildIndexLocked it never reverts, so
 104        // re-checking under the lock would be a dead branch.
 3871105        lock (_lock)
 106        {
 3871107            return TryGetIndexSlot(name, out var slot) ? _items![slot] : null;
 108        }
 3871109    }
 110
 111    /// <summary>Find a child by name (case-insensitive). String overload avoids span/string round-tripping.</summary>
 112    internal FieldDefinition? Find(string name)
 113    {
 13620114        if (Volatile.Read(ref _index) == null)
 115        {
 13500116            var count = Volatile.Read(ref _count);
 13500117            var items = Volatile.Read(ref _items);
 14721118            if (items == null) return null;
 31422119            for (int i = 0; i < count; i++)
 120            {
 14994121                if (string.Equals(items[i].Name, name, StringComparison.OrdinalIgnoreCase))
 11562122                    return items[i];
 123            }
 717124            return null;
 125        }
 126
 120127        lock (_lock)
 128        {
 120129            return _index!.TryGetValue(name, out var slot) ? _items![slot] : null;
 130        }
 120131    }
 132
 133    internal bool TryGetValue(ReadOnlySpan<char> name, [MaybeNullWhen(false)] out FieldDefinition value)
 134    {
 155385135        value = Find(name)!;
 155385136        return value != null;
 137    }
 138
 139    internal bool TryGetValue(string name, [MaybeNullWhen(false)] out FieldDefinition value)
 140    {
 2382141        value = Find(name)!;
 2382142        return value != null;
 143    }
 144
 145    bool IReadOnlyDictionary<string, FieldDefinition>.TryGetValue(string key, [MaybeNullWhen(false)] out FieldDefinition
 366146        => TryGetValue(key, out value);
 147
 148    bool IReadOnlyDictionary<string, FieldDefinition>.ContainsKey(string key)
 606149        => Find(key) != null;
 150
 151    FieldDefinition IReadOnlyDictionary<string, FieldDefinition>.this[string key]
 152    {
 153        get
 154        {
 1863155            var found = Find(key);
 1863156            return found ?? throw new KeyNotFoundException($"Key '{key}' not found.");
 157        }
 158    }
 159
 160    // ── Mutation ──────────────────────────────────────────────────────────────
 161
 162    /// <summary>
 163    /// Appends a new child. The caller must ensure the name does not already exist
 164    /// (use <see cref="Set(string,FieldDefinition)"/> when an update may be needed).
 165    /// </summary>
 166    internal void Append(FieldDefinition child)
 167    {
 174351168        lock (_lock)
 169        {
 174351170            AppendLocked(child);
 174351171        }
 174351172    }
 173
 174    /// <summary>Adds or replaces a child by name (case-insensitive).</summary>
 175    internal void Set(string name, FieldDefinition child)
 176    {
 11592177        lock (_lock)
 178        {
 11592179            var items = _items;
 11592180            if (items != null)
 181            {
 8952182                if (_index != null)
 183                {
 7515184                    if (_index.TryGetValue(name, out var slot))
 185                    {
 39186                        items[slot] = child;
 39187                        UpdateIndexKeyLocked(name, child.Name, slot);
 39188                        return;
 189                    }
 190                }
 191                else
 192                {
 5472193                    for (int i = 0; i < _count; i++)
 194                    {
 1572195                        if (string.Equals(items[i].Name, name, StringComparison.OrdinalIgnoreCase))
 196                        {
 273197                            items[i] = child;
 273198                            return;
 199                        }
 200                    }
 201                }
 202            }
 11280203            AppendLocked(child);
 11280204        }
 11592205    }
 206
 207    /// <summary>
 208    /// Replaces an existing child by span name (case-insensitive). The only call site —
 209    /// <c>FieldFactory.ProcessDottedSegment</c> — invokes this strictly after a successful
 210    /// <see cref="TryGetValue(ReadOnlySpan{char},out FieldDefinition)"/>, so the entry is
 211    /// guaranteed to exist; below the index threshold the loop simply walks to the position of
 212    /// the known match, and once indexed the index resolves that position directly.
 213    /// </summary>
 214    internal void Set(ReadOnlySpan<char> name, FieldDefinition child)
 215    {
 405216        lock (_lock)
 217        {
 405218            var items = _items!;
 405219            if (_index != null)
 220            {
 221                // Documented precondition (see method summary): name is guaranteed to already be a
 222                // key, so the slot lookup itself never allocates a fallback string.
 9223                TryGetIndexSlot(name, out var slot);
 9224                items[slot] = child;
 9225                UpdateIndexKeyLocked(name, child.Name, slot);
 9226                return;
 227            }
 228
 396229            int i = 0;
 996230            while (!name.Equals(items[i].Name.AsSpan(), StringComparison.OrdinalIgnoreCase)) i++;
 396231            items[i] = child;
 396232        }
 405233    }
 234
 235    /// <summary>
 236    /// Probes <see cref="_index"/> by span without allocating a lookup string on .NET 9+ (via
 237    /// <c>Dictionary&lt;TKey,TValue&gt;.GetAlternateLookup&lt;ReadOnlySpan&lt;char&gt;&gt;()</c>,
 238    /// which <see cref="StringComparer.OrdinalIgnoreCase"/> supports as an
 239    /// <c>IAlternateEqualityComparer&lt;ReadOnlySpan&lt;char&gt;, string&gt;</c>); falls back to
 240    /// <see cref="ReadOnlySpan{T}.ToString"/> on net8.0, where the alternate-lookup API does not
 241    /// exist. Must be called under <see cref="_lock"/> with <see cref="_index"/> known non-null.
 242    /// The lookup struct itself is a zero-allocation view over the live dictionary — obtained
 243    /// fresh on every call rather than cached, so it can never be used against a stale/rebuilt
 244    /// <see cref="_index"/> instance.
 245    /// </summary>
 246    private bool TryGetIndexSlot(ReadOnlySpan<char> name, out int slot)
 247    {
 248#if NET9_0_OR_GREATER
 920249        return _index!.GetAlternateLookup<ReadOnlySpan<char>>().TryGetValue(name, out slot);
 250#else
 2960251        return _index!.TryGetValue(name.ToString(), out slot);
 252#endif
 253    }
 254
 255    /// <summary>
 256    /// Removes a key from <see cref="_index"/> by span, same allocation trade-off as
 257    /// <see cref="TryGetIndexSlot"/>. Must be called under <see cref="_lock"/> with
 258    /// <see cref="_index"/> known non-null.
 259    /// </summary>
 260    private void RemoveIndexKey(ReadOnlySpan<char> name)
 261    {
 262#if NET9_0_OR_GREATER
 22263        _index!.GetAlternateLookup<ReadOnlySpan<char>>().Remove(name);
 264#else
 11265        _index!.Remove(name.ToString());
 266#endif
 33267    }
 268
 269    /// <summary>
 270    /// After an in-place replace, keeps <see cref="_index"/> consistent when the replacement's
 271    /// name differs from the looked-up key (e.g. alias/name changes on the field object) — removes
 272    /// the stale key and (re)inserts the new one pointing at the same slot. Must be called under
 273    /// <see cref="_lock"/> with <see cref="_index"/> known non-null.
 274    /// </summary>
 275    private void UpdateIndexKeyLocked(ReadOnlySpan<char> oldKey, string newKey, int slot)
 276    {
 57277        if (!oldKey.Equals(newKey.AsSpan(), StringComparison.OrdinalIgnoreCase))
 278        {
 33279            RemoveIndexKey(oldKey);
 280        }
 57281        _index![newKey] = slot;
 57282    }
 283
 284    /// <summary>
 285    /// Replaces <paramref name="oldChild"/> with <paramref name="newChild"/> in the exact slot
 286    /// <paramref name="oldChild"/> occupies, identified by REFERENCE — never by name. Name-keyed
 287    /// lookup/replace cannot disambiguate two children that legitimately share a
 288    /// <see cref="FieldDefinition.Name"/> but differ by alias (distinct GraphQL response keys); a
 289    /// caller that already holds the exact matched instance (e.g. after an alias-aware scan) uses
 290    /// this to guarantee the replace lands on that instance's own slot, preserving both its
 291    /// position (render order) and every OTHER same-named sibling untouched.
 292    /// </summary>
 293    /// <remarks>
 294    /// Common case is O(1): <see cref="_index"/> is keyed by <see cref="FieldDefinition.Name"/>, and
 295    /// its remembered slot for <paramref name="oldChild"/>'s name almost always already holds
 296    /// <paramref name="oldChild"/> itself, since a caller only reaches here after a same-name hit.
 297    /// Only when that slot no longer holds <paramref name="oldChild"/> (a genuine same-name/
 298    /// different-alias collision, where the index can remember only one of the colliding slots) does
 299    /// this fall back to a linear scan by reference — exactly the same rare case that already forces
 300    /// a linear scan in <see cref="NGql.Core.Extensions.Helpers.FindExistingField(FieldChildren, FieldDefinition)"/>'s
 301    /// slow path.
 302    /// </remarks>
 303    internal void ReplaceReference(FieldDefinition oldChild, FieldDefinition newChild)
 304    {
 495305        lock (_lock)
 306        {
 495307            var items = _items!;
 308
 495309            if (_index != null && _index.TryGetValue(oldChild.Name, out var hintSlot)
 495310                && ReferenceEquals(items[hintSlot], oldChild))
 311            {
 6312                items[hintSlot] = newChild;
 6313                UpdateIndexKeyLocked(oldChild.Name.AsSpan(), newChild.Name, hintSlot);
 6314                return;
 315            }
 316
 1506317            for (int i = 0; i < _count; i++)
 318            {
 750319                if (ReferenceEquals(items[i], oldChild))
 320                {
 486321                    items[i] = newChild;
 486322                    if (_index != null)
 323                    {
 3324                        UpdateIndexKeyLocked(oldChild.Name.AsSpan(), newChild.Name, i);
 325                    }
 486326                    return;
 327                }
 328            }
 3329        }
 495330    }
 331
 332    /// <summary>
 333    /// Supports collection initializer syntax: <c>new FieldChildren { new FieldDefinition("x") }</c>.
 334    /// Delegates to <see cref="Append"/>.
 335    /// </summary>
 51336    internal void Add(FieldDefinition child) => Append(child);
 337
 338    // ── Private helpers ───────────────────────────────────────────────────────
 339
 340    /// <summary>
 341    /// Append logic used within locked sections. Publishes the new slot before bumping
 342    /// <see cref="_count"/> so concurrent readers always see fully-initialized data.
 343    /// </summary>
 344    private void AppendLocked(FieldDefinition child)
 345    {
 185631346        var items = _items;
 185631347        if (items == null)
 348        {
 146382349            items = new FieldDefinition[InitialCapacity];
 146382350            Volatile.Write(ref _items, items);
 351        }
 39249352        else if (_count == items.Length)
 353        {
 354            // Grow: allocate a new array, copy, then publish. Concurrent readers either see the old
 355            // array (with their bounded count) or the new array — both are coherent snapshots.
 420356            var grown = new FieldDefinition[items.Length * 2];
 420357            Array.Copy(items, grown, _count);
 420358            items = grown;
 420359            Volatile.Write(ref _items, items);
 360        }
 361
 185631362        var newSlot = _count;
 185631363        items[newSlot] = child;
 364        // Release-store the new count last so any reader that observes it also sees the slot above.
 185631365        Volatile.Write(ref _count, newSlot + 1);
 366
 185631367        if (_index != null)
 368        {
 23112369            _index[child.Name] = newSlot;
 370        }
 162519371        else if (_count >= IndexThreshold)
 372        {
 99373            BuildIndexLocked();
 374        }
 162519375    }
 376
 377    private void BuildIndexLocked()
 378    {
 99379        var built = new Dictionary<string, int>(_count, StringComparer.OrdinalIgnoreCase);
 3366380        for (int i = 0; i < _count; i++)
 1584381            built[_items![i].Name] = i;
 382        // Publish the index pointer last so readers that observe non-null _index see a fully populated dict.
 99383        Volatile.Write(ref _index, built);
 99384    }
 385
 386    // ── IReadOnlyDictionary (Keys / Values / Enumerator) ─────────────────────
 387
 388    IEnumerable<string> IReadOnlyDictionary<string, FieldDefinition>.Keys
 389    {
 390        get
 391        {
 33392            var count = Volatile.Read(ref _count);
 33393            var items = Volatile.Read(ref _items);
 36394            if (items == null) return [];
 30395            var keys = new List<string>(count);
 186396            for (int i = 0; i < count; i++)
 63397                keys.Add(items[i].Name);
 30398            return keys;
 399        }
 400    }
 401
 402    IEnumerable<FieldDefinition> IReadOnlyDictionary<string, FieldDefinition>.Values
 403    {
 404        get
 405        {
 6406            var count = Volatile.Read(ref _count);
 6407            var items = Volatile.Read(ref _items);
 9408            if (items == null) return [];
 3409            var values = new List<FieldDefinition>(count);
 18410            for (int i = 0; i < count; i++)
 6411                values.Add(items[i]);
 3412            return values;
 413        }
 414    }
 415
 416    /// <summary>
 417    /// Returns a zero-alloc struct enumerator over a snapshot of this collection.
 418    /// </summary>
 419    public FieldChildrenEnumerator GetEnumerator()
 420    {
 1068421        var count = Volatile.Read(ref _count);
 1068422        var items = Volatile.Read(ref _items);
 1068423        return new FieldChildrenEnumerator(items, count);
 424    }
 425
 426    // Explicit interface implementation — provides fallback for code that uses IEnumerable<T> directly
 427    IEnumerator<KeyValuePair<string, FieldDefinition>> IEnumerable<KeyValuePair<string, FieldDefinition>>.GetEnumerator(
 1041428        => GetEnumerator();
 429
 430    IEnumerator IEnumerable.GetEnumerator()
 9431        => GetEnumerator();
 432}
 433
 434/// <summary>
 435/// Zero-allocation struct enumerator for <see cref="FieldChildren"/>.
 436/// Operates on a snapshot taken at construction time, so concurrent appends do not
 437/// affect an in-flight enumeration.
 438/// </summary>
 439internal struct FieldChildrenEnumerator : IEnumerator<KeyValuePair<string, FieldDefinition>>
 440{
 441    private readonly FieldDefinition[]? _snapshot;
 442    private readonly int _count;
 443    private int _index;
 444
 445    internal FieldChildrenEnumerator(FieldDefinition[]? snapshot, int count)
 446    {
 447        _snapshot = snapshot;
 448        _count = count;
 449        _index = -1;
 450    }
 451
 452    public KeyValuePair<string, FieldDefinition> Current
 453    {
 454        get
 455        {
 456            if (_snapshot == null || _index < 0 || _index >= _count)
 457                throw new InvalidOperationException("Enumeration has not started or has ended.");
 458            var item = _snapshot[_index];
 459            return new KeyValuePair<string, FieldDefinition>(item.Name, item);
 460        }
 461    }
 462
 463    object System.Collections.IEnumerator.Current => Current;
 464
 465    public bool MoveNext()
 466    {
 467        if (_snapshot == null) return false;
 468        _index++;
 469        return _index < _count;
 470    }
 471
 472    public void Reset() => _index = -1;
 473
 474    public void Dispose() { }
 475}

Methods/Properties

.ctor()
.ctor(System.Int32)
System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<System.String,NGql.Core.Abstractions.FieldDefinition>>.get_Count()
get_Count()
AsSpan()
Find(System.ReadOnlySpan`1<System.Char>)
Find(System.String)
TryGetValue(System.ReadOnlySpan`1<System.Char>,NGql.Core.Abstractions.FieldDefinition&)
TryGetValue(System.String,NGql.Core.Abstractions.FieldDefinition&)
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.TryGetValue(System.String,NGql.Core.Abstractions.FieldDefinition&)
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.ContainsKey(System.String)
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Item(System.String)
Append(NGql.Core.Abstractions.FieldDefinition)
Set(System.String,NGql.Core.Abstractions.FieldDefinition)
Set(System.ReadOnlySpan`1<System.Char>,NGql.Core.Abstractions.FieldDefinition)
TryGetIndexSlot(System.ReadOnlySpan`1<System.Char>,System.Int32&)
TryGetIndexSlot(System.ReadOnlySpan`1<System.Char>,System.Int32&)
RemoveIndexKey(System.ReadOnlySpan`1<System.Char>)
RemoveIndexKey(System.ReadOnlySpan`1<System.Char>)
UpdateIndexKeyLocked(System.ReadOnlySpan`1<System.Char>,System.String,System.Int32)
ReplaceReference(NGql.Core.Abstractions.FieldDefinition,NGql.Core.Abstractions.FieldDefinition)
Add(NGql.Core.Abstractions.FieldDefinition)
AppendLocked(NGql.Core.Abstractions.FieldDefinition)
BuildIndexLocked()
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Keys()
System.Collections.Generic.IReadOnlyDictionary<System.String,NGql.Core.Abstractions.FieldDefinition>.get_Values()
GetEnumerator()
System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.String,NGql.Core.Abstractions.FieldDefinition>>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()