< Summary

Information
Class: Trailblazer.Pathing.NavigationFlowFieldPayloadCache
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/Flow/NavigationFlowFieldPayloadCache.cs
Line coverage
100%
Covered lines: 701
Uncovered lines: 0
Coverable lines: 701
Total lines: 1245
Line coverage: 100%
Branch coverage
100%
Covered branches: 354
Total branches: 354
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%1818100%
get_Count()100%11100%
get_ImmediateRayWorkspace()100%11100%
get_World()100%11100%
get_CachedBytes()100%11100%
get_LeasedBytes()100%11100%
get_DetachedBytes()100%11100%
get_ReservedPayloadBytes()100%11100%
get_ActiveLeaseCount()100%11100%
get_ReservedLeaseCount()100%11100%
get_MaximumSinglePayloadBytes()100%11100%
TryCreateGuide(...)100%2020100%
IsBoundGuideCurrent(...)100%11100%
ResolveBoundGuideValidation(...)100%22100%
TryReservePayload(...)100%1010100%
ReleasePayloadReservation(...)100%66100%
TryCheckout(...)100%2020100%
ResolveValidatedProof(...)100%22100%
IsExactProofCurrent(...)100%1010100%
TryPublishOrPromote(...)100%2424100%
TryGetPayload(...)100%88100%
TryGetGuidePayload(...)100%88100%
Return(...)100%1010100%
ReturnDetachedGuide(...)100%44100%
RetainGuideIfReusable(...)100%66100%
RemoveExact(...)100%11100%
Reset()100%22100%
Dispose()100%22100%
TryFinishExisting(...)100%44100%
PublishCandidate(...)100%1212100%
TryCheckout(...)100%66100%
ConvertReservation(...)100%11100%
ActivateEntry(...)100%44100%
RentGuide(...)100%22100%
CanActivate(...)100%22100%
CanActivateNew(...)100%11100%
ClassifyCoverage(...)100%1010100%
ComparePrefixes(...)100%3232100%
IsWorldCurrent(...)100%22100%
IsPayloadCurrent(...)100%22100%
CompletePostLockValidation(...)100%22100%
DependenciesAreEqual(...)100%66100%
DependenciesAreSubset(...)100%66100%
WorldDependencyIsSubset(...)100%22100%
NodeTransitionInstructionsAreEqual(...)100%22100%
TransitionInstructionsAreEqual(...)100%2020100%
ComponentDependenciesAreSubset(...)100%1010100%
PageDependenciesAreSubset(...)100%1010100%
ComparePageAddress(...)100%22100%
EvictFor(...)100%88100%
Insert(...)100%11100%
TryIssueLeaseSlot(...)100%22100%
TryGetReservationSlot(...)100%66100%
ReleaseReservationSlot(...)100%11100%
RecycleLeaseSlot(...)100%11100%
RecycleLeaseSlotIdentity(...)100%22100%
InvalidateExact(...)100%1414100%
ResetUnderLock()100%1010100%
FindSlot(...)100%1414100%
Touch(...)100%22100%
LinkMostRecent(...)100%22100%
Unlink(...)100%44100%
RemoveAt(...)100%22100%
.ctor(...)100%11100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/Flow/NavigationFlowFieldPayloadCache.cs

#LineLine coverage
 1//=======================================================================
 2// NavigationFlowFieldPayloadCache.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using GridForge.Grids;
 10
 11namespace Trailblazer.Pathing;
 12
 13internal readonly struct NavigationFlowFieldReservation
 14{
 15    internal NavigationFlowFieldReservation(
 16        NavigationFlowFieldPayloadCache owner,
 17        int slot,
 18        ulong generation)
 19    {
 20        Owner = owner;
 21        Slot = slot;
 22        Generation = generation;
 23    }
 24
 25    internal readonly NavigationFlowFieldPayloadCache? Owner;
 26    internal readonly int Slot;
 27    internal readonly ulong Generation;
 28}
 29
 30/// <summary>Stores bounded destination-centric canonical flow-field prefixes.</summary>
 31internal sealed class NavigationFlowFieldPayloadCache : IDisposable
 32{
 33    internal enum LeaseSlotState : byte
 34    {
 35        Free = 0,
 36        Reserved = 1,
 37        ActivePayload = 2,
 38        ActiveGuide = 3,
 39        Retired = 4
 40    }
 41
 42    private enum PrefixRelation : byte
 43    {
 44        Equal = 0,
 45        CandidateLonger = 1,
 46        ExistingLonger = 2
 47    }
 48
 119149    private readonly object _sync = new();
 50    private readonly GridWorld _world;
 51    private readonly NavigationFlowFieldPayloadKey[] _keys;
 52    private readonly CacheEntry?[] _entries;
 53    private readonly byte[] _states;
 54    private readonly int[] _previous;
 55    private readonly int[] _next;
 56    private readonly LeaseSlot[] _leaseSlots;
 57    private readonly int[] _freeLeaseSlots;
 58    private readonly int _mask;
 59    private readonly int _maximumEntries;
 60    private readonly long _maximumReusableBytes;
 61    private readonly long _maximumSinglePayloadBytes;
 62    private readonly long _maximumActivePayloadBytes;
 63    private int _freeLeaseCount;
 119164    private int _leastRecent = -1;
 119165    private int _mostRecent = -1;
 66    private int _count;
 67    private int _activeLeaseCount;
 68    private int _reservedLeaseCount;
 69    private long _cachedBytes;
 70    private long _leasedBytes;
 71    private long _detachedBytes;
 72    private long _reservedPayloadBytes;
 73    private bool _disposed;
 74    private NavigationFlowFieldGuideLease?[] _freeGuides;
 75    private int _freeGuideCount;
 76    private readonly int _guideMapCapacity;
 77    private readonly NavigationImmediateRayWorkspace _immediateRayWorkspace;
 78
 119179    internal NavigationFlowFieldPayloadCache(
 119180        GridWorld world,
 119181        int maxEntries,
 119182        long maxReusableBytes,
 119183        long maxSinglePayloadBytes,
 119184        long maxActivePayloadBytes,
 119185        int maxActiveLeases,
 119186        int guideMapCapacity,
 119187        NavigationImmediateRayWorkspace immediateRayWorkspace)
 88    {
 119189        SwiftThrowHelper.ThrowIfNull(world, nameof(world));
 119190        SwiftThrowHelper.ThrowIfNegative(maxEntries, nameof(maxEntries));
 119191        if (maxReusableBytes < 0)
 192            throw new ArgumentOutOfRangeException(nameof(maxReusableBytes));
 119093        if (maxSinglePayloadBytes < 0)
 194            throw new ArgumentOutOfRangeException(nameof(maxSinglePayloadBytes));
 118995        if (maxActivePayloadBytes < 0)
 196            throw new ArgumentOutOfRangeException(nameof(maxActivePayloadBytes));
 118897        SwiftThrowHelper.ThrowIfNegative(maxActiveLeases, nameof(maxActiveLeases));
 118898        SwiftThrowHelper.ThrowIfNegative(guideMapCapacity, nameof(guideMapCapacity));
 118899        SwiftThrowHelper.ThrowIfNull(immediateRayWorkspace, nameof(immediateRayWorkspace));
 1188100        SwiftThrowHelper.ThrowIfArgument(
 1188101            maxSinglePayloadBytes > maxActivePayloadBytes,
 1188102            nameof(maxSinglePayloadBytes),
 1188103            "A single payload cannot exceed the complete active-payload byte ceiling.");
 104
 1188105        int tableSize = 1;
 1188106        int required = checked(Math.Max(1, maxEntries * 2));
 9343107        while (tableSize < required)
 8155108            tableSize = checked(tableSize * 2);
 1188109        _world = world;
 1188110        _keys = new NavigationFlowFieldPayloadKey[tableSize];
 1188111        _entries = new CacheEntry[tableSize];
 1188112        _states = new byte[tableSize];
 1188113        _previous = new int[tableSize];
 1188114        _next = new int[tableSize];
 1188115        Array.Fill(_previous, -1);
 1188116        Array.Fill(_next, -1);
 1188117        _leaseSlots = maxActiveLeases == 0
 1188118            ? Array.Empty<LeaseSlot>()
 1188119            : new LeaseSlot[maxActiveLeases];
 1188120        _freeLeaseSlots = maxActiveLeases == 0
 1188121            ? Array.Empty<int>()
 1188122            : new int[maxActiveLeases];
 18818123        for (int i = 0; i < _freeLeaseSlots.Length; i++)
 8221124            _freeLeaseSlots[i] = _freeLeaseSlots.Length - i - 1;
 1188125        _freeLeaseCount = _freeLeaseSlots.Length;
 1188126        _freeGuides = maxActiveLeases == 0
 1188127            ? Array.Empty<NavigationFlowFieldGuideLease?>()
 1188128            : new NavigationFlowFieldGuideLease[maxActiveLeases];
 18818129        for (int i = 0; i < _freeGuides.Length; i++)
 8221130            _freeGuides[i] = new NavigationFlowFieldGuideLease(guideMapCapacity);
 1188131        _freeGuideCount = _freeGuides.Length;
 1188132        _guideMapCapacity = guideMapCapacity;
 1188133        _immediateRayWorkspace = immediateRayWorkspace;
 1188134        _mask = tableSize - 1;
 1188135        _maximumEntries = maxEntries;
 1188136        _maximumReusableBytes = maxReusableBytes;
 1188137        _maximumSinglePayloadBytes = maxSinglePayloadBytes;
 1188138        _maximumActivePayloadBytes = maxActivePayloadBytes;
 1188139    }
 140
 141    internal int Count
 142    {
 213143        get { lock (_sync) return _count; }
 144    }
 145
 146    internal NavigationImmediateRayWorkspace ImmediateRayWorkspace =>
 1379147        _immediateRayWorkspace;
 148
 9202149    internal GridWorld World => _world;
 150
 151    internal long CachedBytes
 152    {
 81153        get { lock (_sync) return _cachedBytes; }
 154    }
 155
 156    internal long LeasedBytes
 157    {
 129158        get { lock (_sync) return _leasedBytes; }
 159    }
 160
 161    internal long DetachedBytes
 162    {
 75163        get { lock (_sync) return _detachedBytes; }
 164    }
 165
 166    internal long ReservedPayloadBytes
 167    {
 63168        get { lock (_sync) return _reservedPayloadBytes; }
 169    }
 170
 171    internal int ActiveLeaseCount
 172    {
 354173        get { lock (_sync) return _activeLeaseCount; }
 174    }
 175
 176    internal int ReservedLeaseCount
 177    {
 165178        get { lock (_sync) return _reservedLeaseCount; }
 179    }
 180
 39181    internal long MaximumSinglePayloadBytes => _maximumSinglePayloadBytes;
 182
 183    internal NavigationGuideStatus TryCreateGuide(
 184        NavigationWorldGraphStore store,
 185        NavigationFlowQueryResult result,
 186        out NavigationFlowFieldLease guide)
 187    {
 1185188        SwiftThrowHelper.ThrowIfNull(store, nameof(store));
 1185189        guide = default;
 1185190        NavigationFlowFieldPayloadLease payloadLease = result.PayloadLease;
 1185191        NavigationFlowFieldGuideLease? inner = null;
 1185192        NavigationFlowFieldPayload? payload = null;
 1185193        NavigationGuideStatus status = NavigationGuideStatus.Stale;
 1185194        int slot = -1;
 1185195        ulong generation = 0;
 1185196        FixedMathSharp.Fixed64 originIntegrationCost = FixedMathSharp.Fixed64.Zero;
 1185197        lock (_sync)
 198        {
 1185199            if (!_disposed
 1185200                && payloadLease.IsOwnedBy(this, out slot, out generation))
 201            {
 1184202                ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 1184203                if (leaseSlot.Generation == generation
 1184204                    && leaseSlot.State == LeaseSlotState.ActivePayload
 1184205                    && leaseSlot.Entry != null
 1184206                    && !leaseSlot.Entry.IsInvalidated)
 207                {
 1184208                    payload = leaseSlot.Entry.Payload;
 1184209                    NavigationWorldGraph current = store.Current;
 1184210                    if (current.MapCount > _guideMapCapacity)
 211                    {
 3212                        status = NavigationGuideStatus.CapacityExceeded;
 213                    }
 1181214                    else if (!IsPayloadCurrent(current, payload))
 215                    {
 1216                        leaseSlot.Entry.IsInvalidated = true;
 217                    }
 1180218                    else if (!payload.TryGetNode(
 1180219                            result.ResolvedOrigin,
 1180220                            payload.Key.StartMedium,
 1180221                            out NavigationFlowFieldNode origin))
 222                    {
 1223                        status = NavigationGuideStatus.Stale;
 224                    }
 225                    else
 226                    {
 1179227                        inner = RentGuide(
 1179228                            _freeGuides,
 1179229                            ref _freeGuideCount,
 1179230                            _guideMapCapacity);
 1179231                        leaseSlot.State = LeaseSlotState.ActiveGuide;
 1179232                        originIntegrationCost = origin.IntegrationCost;
 1179233                        status = NavigationGuideStatus.Success;
 234                    }
 235                }
 236            }
 1180237        }
 1185238        if (status != NavigationGuideStatus.Success)
 239        {
 6240            payloadLease.Dispose();
 6241            return status;
 242        }
 1179243        NavigationFlowFieldGuideLease activeGuide = inner!;
 1179244        NavigationFlowFieldPayload activePayload = payload!;
 1179245        activeGuide.Bind(
 1179246            this,
 1179247            store,
 1179248            slot,
 1179249            generation,
 1179250            result.ResolvedOrigin,
 1179251            activePayload.Key.StartMedium,
 1179252            originIntegrationCost);
 1179253        guide = new NavigationFlowFieldLease(activeGuide);
 1179254        bool currentAfterBind = IsBoundGuideCurrent(
 1179255            TryGetGuidePayload(slot, generation, out _),
 1179256            IsPayloadCurrent(store.Current, activePayload));
 1179257        return ResolveBoundGuideValidation(currentAfterBind, ref guide);
 258    }
 259
 260    internal static bool IsBoundGuideCurrent(
 261        NavigationFlowFieldStatus leaseStatus,
 262        bool payloadCurrent) =>
 1183263        leaseStatus == NavigationFlowFieldStatus.Success && payloadCurrent;
 264
 265    internal static NavigationGuideStatus ResolveBoundGuideValidation(
 266        bool current,
 267        ref NavigationFlowFieldLease guide)
 268    {
 1181269        if (current)
 1180270            return NavigationGuideStatus.Success;
 1271        guide.Dispose();
 1272        guide = default;
 1273        return NavigationGuideStatus.Stale;
 274    }
 275
 276    internal bool TryReservePayload(
 277        long maximumBytes,
 278        out NavigationFlowFieldReservation reservation)
 279    {
 555280        if (maximumBytes < 0)
 1281            throw new ArgumentOutOfRangeException(nameof(maximumBytes));
 554282        reservation = default;
 554283        if (maximumBytes > _maximumSinglePayloadBytes)
 3284            return false;
 551285        lock (_sync)
 286        {
 551287            if (_disposed
 551288                || maximumBytes > _maximumActivePayloadBytes
 551289                    - _leasedBytes
 551290                    - _reservedPayloadBytes
 551291                || !TryIssueLeaseSlot(out int slot, out ulong generation))
 292            {
 4293                return false;
 294            }
 547295            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 547296            leaseSlot.State = LeaseSlotState.Reserved;
 547297            leaseSlot.MaximumBytes = maximumBytes;
 547298            _reservedLeaseCount++;
 547299            _reservedPayloadBytes = checked(_reservedPayloadBytes + maximumBytes);
 547300            reservation = new NavigationFlowFieldReservation(this, slot, generation);
 547301            return true;
 302        }
 551303    }
 304
 305    internal void ReleasePayloadReservation(
 306        ref NavigationFlowFieldReservation reservation)
 307    {
 442308        if (reservation.Owner == null)
 309        {
 131310            reservation = default;
 131311            return;
 312        }
 311313        if (!ReferenceEquals(reservation.Owner, this))
 1314            return;
 310315        NavigationFlowFieldReservation released = reservation;
 310316        reservation = default;
 310317        lock (_sync)
 318        {
 310319            if (TryGetReservationSlot(released, out int slot))
 307320                ReleaseReservationSlot(slot);
 310321        }
 310322    }
 323
 324    internal NavigationFlowFieldStatus TryCheckout(
 325        NavigationWorldGraphStore store,
 326        NavigationWorldGraph expectedGraph,
 327        NavigationFlowFieldPayloadKey key,
 328        NavigationCellAddress requiredOrigin,
 329        out NavigationFlowFieldPayloadLease lease,
 330        out NavigationFlowFieldPayload? proof)
 331    {
 1425332        SwiftThrowHelper.ThrowIfNull(store, nameof(store));
 1425333        SwiftThrowHelper.ThrowIfNull(expectedGraph, nameof(expectedGraph));
 1425334        lease = default;
 1425335        proof = null;
 1425336        NavigationFlowFieldPayload? checkedPayload = null;
 337        NavigationFlowFieldStatus status;
 1425338        lock (_sync)
 339        {
 1425340            if (_disposed)
 1341                return NavigationFlowFieldStatus.Stale;
 1424342            FindSlot(key, out int slot, out bool found);
 1424343            if (!found)
 57344                return NavigationFlowFieldStatus.Pending;
 1367345            CacheEntry entry = _entries[slot]!;
 1367346            NavigationWorldGraph graph = store.Current;
 1367347            if (!IsPayloadCurrent(graph, entry.Payload))
 348            {
 3349                RemoveAt(slot, invalidate: true);
 3350                return NavigationFlowFieldStatus.Stale;
 351            }
 1364352            if (!IsPayloadCurrent(expectedGraph, entry.Payload))
 1353                return NavigationFlowFieldStatus.Pending;
 1363354            checkedPayload = entry.Payload;
 1363355            status = ClassifyCoverage(checkedPayload, requiredOrigin);
 1363356            if (status == NavigationFlowFieldStatus.Success)
 357            {
 1356358                if (!TryCheckout(entry, out lease))
 5359                    return NavigationFlowFieldStatus.CapacityExceeded;
 1351360                Touch(slot);
 361            }
 7362            else if (status is NavigationFlowFieldStatus.NoPath
 7363                or NavigationFlowFieldStatus.CostOverflow)
 364            {
 6365                Touch(slot);
 366            }
 7367        }
 1358368        if (status != NavigationFlowFieldStatus.Pending)
 369        {
 1357370            status = CompletePostLockValidation(
 1357371                store,
 1357372                checkedPayload!,
 1357373                status,
 1357374                ref lease);
 1357375            proof = ResolveValidatedProof(status, checkedPayload);
 376        }
 1358377        return status;
 67378    }
 379
 380    internal static NavigationFlowFieldPayload? ResolveValidatedProof(
 381        NavigationFlowFieldStatus status,
 1361382        NavigationFlowFieldPayload payload) => status == NavigationFlowFieldStatus.Stale
 1361383        ? null
 1361384        : payload;
 385
 386    internal bool IsExactProofCurrent(
 387        NavigationWorldGraphStore store,
 388        NavigationFlowFieldPayload proof,
 389        NavigationCellAddress requiredOrigin,
 390        NavigationFlowFieldStatus expectedStatus)
 391    {
 5392        SwiftThrowHelper.ThrowIfNull(store, nameof(store));
 5393        SwiftThrowHelper.ThrowIfNull(proof, nameof(proof));
 394        bool current;
 5395        lock (_sync)
 396        {
 5397            FindSlot(proof.Key, out int slot, out bool found);
 5398            current = !_disposed
 5399                && found
 5400                && ReferenceEquals(_entries[slot]!.Payload, proof)
 5401                && IsPayloadCurrent(store.Current, proof)
 5402                && ClassifyCoverage(proof, requiredOrigin) == expectedStatus;
 5403        }
 5404        if (current && IsPayloadCurrent(store.Current, proof))
 405        {
 2406            return true;
 407        }
 3408        InvalidateExact(proof);
 3409        return false;
 410    }
 411
 412    internal NavigationFlowFieldStatus TryPublishOrPromote(
 413        NavigationWorldGraphStore store,
 414        NavigationFlowFieldPayload payload,
 415        NavigationCellAddress requiredOrigin,
 416        ref NavigationFlowFieldReservation reservation,
 417        out NavigationFlowFieldPayloadLease lease)
 418    {
 270419        SwiftThrowHelper.ThrowIfNull(store, nameof(store));
 270420        SwiftThrowHelper.ThrowIfNull(payload, nameof(payload));
 270421        lease = default;
 270422        NavigationFlowFieldPayload? canonical = null;
 423        NavigationFlowFieldStatus status;
 270424        lock (_sync)
 425        {
 270426            if (_disposed || !TryGetReservationSlot(reservation, out int reservationSlot))
 1427                return NavigationFlowFieldStatus.Stale;
 269428            ref LeaseSlot reserved = ref _leaseSlots[reservationSlot];
 269429            if (payload.RetainedBytes > reserved.MaximumBytes)
 1430                return NavigationFlowFieldStatus.CapacityExceeded;
 268431            NavigationWorldGraph graph = store.Current;
 268432            if (!IsPayloadCurrent(graph, payload))
 2433                return NavigationFlowFieldStatus.Stale;
 434
 266435            FindSlot(payload.Key, out int currentSlot, out bool found);
 266436            if (found
 266437                && !IsPayloadCurrent(graph, _entries[currentSlot]!.Payload))
 438            {
 1439                RemoveAt(currentSlot, invalidate: true);
 1440                found = false;
 441            }
 442
 266443            status = ClassifyCoverage(payload, requiredOrigin);
 444
 266445            CacheEntry? incumbent = found ? _entries[currentSlot] : null;
 266446            if (incumbent != null)
 447            {
 42448                PrefixRelation relation = ComparePrefixes(incumbent.Payload, payload);
 14449                if (relation is PrefixRelation.Equal or PrefixRelation.ExistingLonger)
 450                {
 5451                    canonical = incumbent.Payload;
 5452                    status = ClassifyCoverage(canonical, requiredOrigin);
 5453                    if (!TryFinishExisting(
 5454                            currentSlot,
 5455                            incumbent,
 5456                            status,
 5457                            reservationSlot,
 5458                            ref reservation,
 5459                            out lease))
 460                    {
 1461                        return NavigationFlowFieldStatus.CapacityExceeded;
 462                    }
 463                }
 464                else
 465                {
 9466                    canonical = payload;
 9467                    PublishCandidate(
 9468                        payload,
 9469                        status,
 9470                        currentSlot,
 9471                        incumbent,
 9472                        reservationSlot,
 9473                        ref reservation,
 9474                        out lease);
 475                }
 476            }
 477            else
 478            {
 224479                canonical = payload;
 224480                PublishCandidate(
 224481                    payload,
 224482                    status,
 224483                    currentSlot: -1,
 224484                    incumbent: null,
 224485                    reservationSlot,
 224486                    ref reservation,
 224487                    out lease);
 488            }
 228489        }
 490
 237491        return CompletePostLockValidation(
 237492            store,
 237493            canonical!,
 237494            status,
 237495            ref lease);
 5496    }
 497
 498    internal NavigationFlowFieldStatus TryGetPayload(
 499        int slot,
 500        ulong generation,
 501        out NavigationFlowFieldPayload payload)
 502    {
 302503        lock (_sync)
 504        {
 302505            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 302506            CacheEntry? entry = leaseSlot.Entry;
 302507            if (leaseSlot.Generation == generation
 302508                && leaseSlot.State == LeaseSlotState.ActivePayload
 302509                && entry != null
 302510                && !entry!.IsInvalidated)
 511            {
 292512                payload = entry!.Payload;
 292513                return NavigationFlowFieldStatus.Success;
 514            }
 10515        }
 10516        payload = null!;
 10517        return NavigationFlowFieldStatus.Stale;
 292518    }
 519
 520    internal NavigationFlowFieldStatus TryGetGuidePayload(
 521        int slot,
 522        ulong generation,
 523        out NavigationFlowFieldPayload payload)
 524    {
 3952525        lock (_sync)
 526        {
 3952527            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 3952528            CacheEntry? entry = leaseSlot.Entry;
 3952529            if (leaseSlot.Generation == generation
 3952530                && leaseSlot.State == LeaseSlotState.ActiveGuide
 3952531                && entry != null
 3952532                && !entry!.IsInvalidated)
 533            {
 3950534                payload = entry!.Payload;
 3950535                return NavigationFlowFieldStatus.Success;
 536            }
 2537        }
 2538        payload = null!;
 2539        return NavigationFlowFieldStatus.Stale;
 3950540    }
 541
 542    internal void Return(int slot, ulong generation)
 543    {
 429544        lock (_sync)
 545        {
 429546            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 429547            bool active = leaseSlot.Generation == generation
 429548                && leaseSlot.State == LeaseSlotState.ActivePayload
 429549                && leaseSlot.Entry != null;
 429550            if (!active)
 551            {
 35552                return;
 553            }
 394554            CacheEntry entry = leaseSlot.Entry!;
 394555            entry.LeaseCount--;
 394556            _activeLeaseCount--;
 394557            if (entry.LeaseCount == 0)
 558            {
 390559                _leasedBytes = checked(_leasedBytes - entry.Payload.RetainedBytes);
 390560                if (!entry.IsCached)
 14561                    _detachedBytes = checked(_detachedBytes - entry.Payload.RetainedBytes);
 562            }
 394563            RecycleLeaseSlot(slot);
 394564        }
 429565    }
 566
 567    internal void ReturnDetachedGuide(
 568        NavigationFlowFieldGuideLease guide,
 569        ulong generation,
 570        int slot)
 571    {
 1178572        lock (_sync)
 573        {
 1178574            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 1178575            CacheEntry entry = leaseSlot.Entry!;
 1178576            entry.LeaseCount--;
 1178577            _activeLeaseCount--;
 1178578            if (entry.LeaseCount == 0)
 579            {
 1171580                _leasedBytes = checked(_leasedBytes - entry.Payload.RetainedBytes);
 1171581                if (!entry.IsCached)
 582                {
 2583                    _detachedBytes = checked(
 2584                        _detachedBytes - entry.Payload.RetainedBytes);
 585                }
 586            }
 1178587            RecycleLeaseSlot(slot);
 1178588            _freeGuideCount = RetainGuideIfReusable(
 1178589                generation,
 1178590                _disposed,
 1178591                guide,
 1178592                _freeGuides,
 1178593                _freeGuideCount);
 1178594        }
 1178595    }
 596
 597    internal static int RetainGuideIfReusable(
 598        ulong generation,
 599        bool disposed,
 600        NavigationFlowFieldGuideLease guide,
 601        NavigationFlowFieldGuideLease?[] freeGuides,
 602        int freeGuideCount)
 603    {
 1182604        if (disposed
 1182605            || !NavigationGenerationCounter.CanAdvance(generation)
 1182606            || freeGuideCount >= freeGuides.Length)
 607        {
 4608            return freeGuideCount;
 609        }
 1178610        freeGuides[freeGuideCount] = guide;
 1178611        return freeGuideCount + 1;
 612    }
 613
 614    internal void RemoveExact(NavigationFlowFieldPayload payload)
 615    {
 3616        SwiftThrowHelper.ThrowIfNull(payload, nameof(payload));
 3617        InvalidateExact(payload);
 3618    }
 619
 620    internal void Reset()
 621    {
 15622        lock (_sync)
 623        {
 15624            if (_disposed)
 1625                return;
 14626            ResetUnderLock();
 14627        }
 15628    }
 629
 630    public void Dispose()
 631    {
 1189632        lock (_sync)
 633        {
 1189634            if (_disposed)
 2635                return;
 1187636            ResetUnderLock();
 1187637            _freeGuides = Array.Empty<NavigationFlowFieldGuideLease?>();
 1187638            _freeGuideCount = 0;
 1187639            _disposed = true;
 1187640        }
 1189641    }
 642
 643    private bool TryFinishExisting(
 644        int currentSlot,
 645        CacheEntry incumbent,
 646        NavigationFlowFieldStatus status,
 647        int reservationSlot,
 648        ref NavigationFlowFieldReservation reservation,
 649        out NavigationFlowFieldPayloadLease lease)
 650    {
 5651        lease = default;
 5652        if (status == NavigationFlowFieldStatus.Success)
 653        {
 4654            if (!CanActivate(incumbent, reservationSlot))
 1655                return false;
 3656            lease = ConvertReservation(
 3657                incumbent,
 3658                reservationSlot,
 3659                ref reservation);
 660        }
 661        else
 662        {
 1663            ReleaseReservationSlot(reservationSlot);
 1664            reservation = default;
 665        }
 4666        Touch(currentSlot);
 4667        return true;
 668    }
 669
 670    private void PublishCandidate(
 671        NavigationFlowFieldPayload payload,
 672        NavigationFlowFieldStatus status,
 673        int currentSlot,
 674        CacheEntry? incumbent,
 675        int reservationSlot,
 676        ref NavigationFlowFieldReservation reservation,
 677        out NavigationFlowFieldPayloadLease lease)
 678    {
 233679        lease = default;
 233680        bool reusable = _maximumEntries > 0
 233681            && payload.RetainedBytes <= _maximumReusableBytes;
 233682        if (!reusable && status != NavigationFlowFieldStatus.Success)
 683        {
 2684            ReleaseReservationSlot(reservationSlot);
 2685            reservation = default;
 2686            return;
 687        }
 688
 689        CacheEntry candidate;
 231690        if (reusable)
 691        {
 229692            EvictFor(payload.RetainedBytes, currentSlot);
 229693            if (incumbent != null)
 9694                RemoveAt(currentSlot, invalidate: false);
 229695            candidate = new CacheEntry(payload, isCached: true);
 229696            Insert(candidate);
 697        }
 698        else
 699        {
 2700            candidate = new CacheEntry(payload, isCached: false);
 701        }
 702
 231703        if (status == NavigationFlowFieldStatus.Success)
 704        {
 221705            lease = ConvertReservation(candidate, reservationSlot, ref reservation);
 706        }
 707        else
 708        {
 10709            ReleaseReservationSlot(reservationSlot);
 10710            reservation = default;
 711        }
 10712    }
 713
 714    private bool TryCheckout(
 715        CacheEntry entry,
 716        out NavigationFlowFieldPayloadLease lease)
 717    {
 1356718        lease = default;
 1356719        if (entry.LeaseCount == 0
 1356720            && entry.Payload.RetainedBytes > _maximumActivePayloadBytes
 1356721                - _leasedBytes
 1356722                - _reservedPayloadBytes)
 723        {
 2724            return false;
 725        }
 1354726        if (!TryIssueLeaseSlot(out int slot, out ulong generation))
 3727            return false;
 1351728        ActivateEntry(entry);
 1351729        ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 1351730        leaseSlot.State = LeaseSlotState.ActivePayload;
 1351731        leaseSlot.Entry = entry;
 1351732        _activeLeaseCount++;
 1351733        lease = new NavigationFlowFieldPayloadLease(this, slot, generation);
 1351734        return true;
 735    }
 736
 737    private NavigationFlowFieldPayloadLease ConvertReservation(
 738        CacheEntry entry,
 739        int reservationSlot,
 740        ref NavigationFlowFieldReservation reservation)
 741    {
 224742        ref LeaseSlot leaseSlot = ref _leaseSlots[reservationSlot];
 224743        _reservedPayloadBytes -= leaseSlot.MaximumBytes;
 224744        _reservedLeaseCount--;
 224745        leaseSlot.MaximumBytes = 0;
 224746        leaseSlot.State = LeaseSlotState.ActivePayload;
 224747        leaseSlot.Entry = entry;
 224748        ActivateEntry(entry);
 224749        _activeLeaseCount++;
 224750        var lease = new NavigationFlowFieldPayloadLease(
 224751            this,
 224752            reservationSlot,
 224753            leaseSlot.Generation);
 224754        reservation = default;
 224755        return lease;
 756    }
 757
 758    private void ActivateEntry(CacheEntry entry)
 759    {
 1575760        if (entry.LeaseCount == 0)
 761        {
 1564762            _leasedBytes = checked(_leasedBytes + entry.Payload.RetainedBytes);
 1564763            if (!entry.IsCached)
 2764                _detachedBytes = checked(_detachedBytes + entry.Payload.RetainedBytes);
 765        }
 1575766        entry.LeaseCount++;
 1575767    }
 768
 769    internal static NavigationFlowFieldGuideLease RentGuide(
 770        NavigationFlowFieldGuideLease?[] freeGuides,
 771        ref int freeGuideCount,
 772        int coveredAddressGenerationCapacity)
 773    {
 1181774        if (freeGuideCount == 0)
 1775            return new NavigationFlowFieldGuideLease(coveredAddressGenerationCapacity);
 1180776        int index = --freeGuideCount;
 1180777        NavigationFlowFieldGuideLease guide = freeGuides[index]!;
 1180778        freeGuides[index] = null;
 1180779        return guide;
 780    }
 781
 782    private bool CanActivate(CacheEntry entry, int reservationSlot) =>
 4783        entry.LeaseCount != 0
 4784        || CanActivateNew(entry.Payload.RetainedBytes, reservationSlot);
 785
 786    private bool CanActivateNew(long retainedBytes, int reservationSlot)
 787    {
 1788        long ownReservation = _leaseSlots[reservationSlot].MaximumBytes;
 1789        long otherReservations = _reservedPayloadBytes - ownReservation;
 1790        return retainedBytes <= _maximumActivePayloadBytes
 1791            - _leasedBytes
 1792            - otherReservations;
 793    }
 794
 795    private static NavigationFlowFieldStatus ClassifyCoverage(
 796        NavigationFlowFieldPayload payload,
 797        NavigationCellAddress requiredOrigin)
 798    {
 1636799        if (!payload.TryGetNode(
 1636800                requiredOrigin,
 1636801                payload.Key.StartMedium,
 1636802                out NavigationFlowFieldNode origin))
 803        {
 14804            return payload.IsComplete
 14805                ? NavigationFlowFieldStatus.NoPath
 14806                : NavigationFlowFieldStatus.Pending;
 807        }
 1622808        if (!FixedMathSharp.Fixed64.TryAdd(
 1622809                origin.IntegrationCost,
 1622810                payload.Key.FlowField.ExtraIntegrationCost,
 1622811                out FixedMathSharp.Fixed64 requiredCost))
 812        {
 8813            return NavigationFlowFieldStatus.CostOverflow;
 814        }
 1614815        return payload.IsComplete || payload.LastSettledCost >= requiredCost
 1614816            ? NavigationFlowFieldStatus.Success
 1614817            : NavigationFlowFieldStatus.Pending;
 818    }
 819
 820    private static PrefixRelation ComparePrefixes(
 821        NavigationFlowFieldPayload existing,
 822        NavigationFlowFieldPayload candidate)
 823    {
 42824        int common = Math.Min(existing.Nodes.Length, candidate.Nodes.Length);
 288825        for (int i = 0; i < common; i++)
 826        {
 118827            NavigationFlowFieldNode left = existing.Nodes[i];
 118828            NavigationFlowFieldNode right = candidate.Nodes[i];
 118829            bool nodesEqual = left.Address == right.Address
 118830                && left.Medium == right.Medium
 118831                && left.IntegrationCost == right.IntegrationCost
 118832                && left.SelectedEdge.Equals(right.SelectedEdge)
 118833                && left.TransitionInstructionOrdinal
 118834                    == right.TransitionInstructionOrdinal
 118835                && NodeTransitionInstructionsAreEqual(
 118836                    existing,
 118837                    left,
 118838                    candidate,
 118839                    right);
 118840            SwiftThrowHelper.ThrowIfTrue(
 118841                !nodesEqual,
 118842                message: "Same-key flow payloads do not share one canonical node prefix.");
 843        }
 844
 26845        if (existing.Nodes.Length == candidate.Nodes.Length)
 846        {
 8847            bool completionEqual = existing.IsComplete == candidate.IsComplete
 8848                && existing.WorldChangeSequence == candidate.WorldChangeSequence
 8849                && existing.TransitionInstructions.Length
 8850                    == candidate.TransitionInstructions.Length
 8851                && DependenciesAreEqual(existing.Dependencies, candidate.Dependencies);
 8852            SwiftThrowHelper.ThrowIfTrue(
 8853                !completionEqual,
 8854                message: "Equal flow prefixes do not share exact completion dependencies.");
 3855            return PrefixRelation.Equal;
 856        }
 857
 18858        NavigationFlowFieldPayload shorter = existing.Nodes.Length < candidate.Nodes.Length
 18859            ? existing
 18860            : candidate;
 18861        NavigationFlowFieldPayload longer = ReferenceEquals(shorter, existing)
 18862            ? candidate
 18863            : existing;
 18864        SwiftThrowHelper.ThrowIfTrue(
 18865            shorter.IsComplete
 18866            || !WorldDependencyIsSubset(shorter, longer)
 18867            || longer.Nodes[shorter.Nodes.Length].IntegrationCost
 18868                <= shorter.LastSettledCost
 18869            || !DependenciesAreSubset(shorter.Dependencies, longer.Dependencies),
 18870            message: "A complete or dependency-incompatible flow payload cannot be a strict prefix.");
 11871        return ReferenceEquals(longer, candidate)
 11872            ? PrefixRelation.CandidateLonger
 11873            : PrefixRelation.ExistingLonger;
 874    }
 875
 876    internal bool IsWorldCurrent(NavigationFlowFieldPayload payload) =>
 9769877        !payload.WorldChangeSequence.HasValue
 9769878        || _world.ChangeSequence == payload.WorldChangeSequence.GetValueOrDefault();
 879
 880    internal bool IsPayloadCurrent(
 881        NavigationWorldGraph graph,
 882        NavigationFlowFieldPayload payload) =>
 9774883        graph.IsDependencyCurrent(payload.Dependencies)
 9774884        && IsWorldCurrent(payload);
 885
 886    internal NavigationFlowFieldStatus CompletePostLockValidation(
 887        NavigationWorldGraphStore store,
 888        NavigationFlowFieldPayload payload,
 889        NavigationFlowFieldStatus status,
 890        ref NavigationFlowFieldPayloadLease lease)
 891    {
 1595892        if (IsPayloadCurrent(store.Current, payload))
 1594893            return status;
 1894        InvalidateExact(payload);
 1895        lease.Dispose();
 1896        lease = default;
 1897        return NavigationFlowFieldStatus.Stale;
 898    }
 899
 900    // Both operands are current against the same graph before prefix comparison,
 901    // so two active transition-rule dependencies necessarily share its one version.
 902    private static bool DependenciesAreEqual(
 903        GraphDependencyStamp left,
 904        GraphDependencyStamp right) =>
 6905        left.Components.Length == right.Components.Length
 6906        && left.Pages.Length == right.Pages.Length
 6907        && left.HasTransitionRuleDependency == right.HasTransitionRuleDependency
 6908        && DependenciesAreSubset(left, right);
 909
 910    internal static bool DependenciesAreSubset(
 911        GraphDependencyStamp shorter,
 912        GraphDependencyStamp longer) =>
 913        // Prefix comparison only runs for payloads with the same key, and that
 914        // key already includes the exact area-policy revision.
 21915        (!shorter.HasTransitionRuleDependency
 21916            || longer.HasTransitionRuleDependency)
 21917        && ComponentDependenciesAreSubset(shorter.Components, longer.Components)
 21918        && PageDependenciesAreSubset(shorter.Pages, longer.Pages);
 919
 920    private static bool WorldDependencyIsSubset(
 921        NavigationFlowFieldPayload shorter,
 922        NavigationFlowFieldPayload longer) =>
 17923        !shorter.WorldChangeSequence.HasValue
 17924        || longer.WorldChangeSequence == shorter.WorldChangeSequence;
 925
 926    private static bool NodeTransitionInstructionsAreEqual(
 927        NavigationFlowFieldPayload leftPayload,
 928        NavigationFlowFieldNode left,
 929        NavigationFlowFieldPayload rightPayload,
 930        NavigationFlowFieldNode right)
 931    {
 114932        int ordinal = left.TransitionInstructionOrdinal;
 114933        if (ordinal < 0)
 102934            return true;
 12935        return TransitionInstructionsAreEqual(
 12936            leftPayload.TransitionInstructions[ordinal],
 12937            rightPayload.TransitionInstructions[ordinal]);
 938    }
 939
 940    private static bool TransitionInstructionsAreEqual(
 941        in NavigationTransitionInstruction left,
 942        in NavigationTransitionInstruction right) =>
 12943        left.IdentityKind == right.IdentityKind
 12944        && string.Equals(left.OwnerMapId, right.OwnerMapId, StringComparison.Ordinal)
 12945        && string.Equals(left.Id, right.Id, StringComparison.Ordinal)
 12946        && left.Type == right.Type
 12947        && left.SourceAddress == right.SourceAddress
 12948        && left.DestinationAddress == right.DestinationAddress
 12949        && left.SourceMedium == right.SourceMedium
 12950        && left.DestinationMedium == right.DestinationMedium
 12951        && left.SourcePosition == right.SourcePosition
 12952        && left.DestinationPosition == right.DestinationPosition
 12953        && left.LocomotionHints == right.LocomotionHints;
 954
 955    private static bool ComponentDependenciesAreSubset(
 956        GraphComponentDependency[] shorter,
 957        GraphComponentDependency[] longer)
 958    {
 19959        int longerIndex = 0;
 68960        for (int i = 0; i < shorter.Length; i++)
 961        {
 16962            GraphComponentDependency expected = shorter[i];
 17963            while (longerIndex < longer.Length
 17964                && longer[longerIndex].Key.CompareTo(expected.Key) < 0)
 965            {
 1966                longerIndex++;
 967            }
 16968            if (longerIndex >= longer.Length
 16969                || !longer[longerIndex].Equals(expected))
 970            {
 1971                return false;
 972            }
 15973            longerIndex++;
 974        }
 18975        return true;
 976    }
 977
 978    private static bool PageDependenciesAreSubset(
 979        GraphPageDependency[] shorter,
 980        GraphPageDependency[] longer)
 981    {
 18982        int longerIndex = 0;
 66983        for (int i = 0; i < shorter.Length; i++)
 984        {
 16985            GraphPageDependency expected = shorter[i];
 18986            while (longerIndex < longer.Length
 18987                && ComparePageAddress(longer[longerIndex], expected) < 0)
 988            {
 2989                longerIndex++;
 990            }
 16991            if (longerIndex >= longer.Length
 16992                || !longer[longerIndex].Equals(expected))
 993            {
 1994                return false;
 995            }
 15996            longerIndex++;
 997        }
 17998        return true;
 999    }
 1000
 1001    private static int ComparePageAddress(
 1002        GraphPageDependency left,
 1003        GraphPageDependency right)
 1004    {
 171005        int mapComparison = string.CompareOrdinal(left.MapId, right.MapId);
 171006        return mapComparison != 0
 171007            ? mapComparison
 171008            : left.PageIndex.CompareTo(right.PageIndex);
 1009    }
 1010
 1011    private void EvictFor(long candidateBytes, int replacedSlot)
 1012    {
 2291013        int prospectiveCount = _count + (replacedSlot >= 0 ? 0 : 1);
 2291014        long prospectiveBytes = checked(
 2291015            _cachedBytes
 2291016            - (replacedSlot >= 0 ? _entries[replacedSlot]!.Payload.RetainedBytes : 0)
 2291017            + candidateBytes);
 2341018        while (prospectiveCount > _maximumEntries
 2341019            || prospectiveBytes > _maximumReusableBytes)
 1020        {
 51021            int eviction = _leastRecent;
 51022            if (eviction == replacedSlot)
 11023                eviction = _next[eviction];
 51024            long bytes = _entries[eviction]!.Payload.RetainedBytes;
 51025            RemoveAt(eviction, invalidate: false);
 51026            prospectiveCount--;
 51027            prospectiveBytes -= bytes;
 1028        }
 2291029    }
 1030
 1031    private void Insert(CacheEntry entry)
 1032    {
 2291033        FindSlot(entry.Payload.Key, out int slot, out _);
 2291034        _states[slot] = 1;
 2291035        _keys[slot] = entry.Payload.Key;
 2291036        _entries[slot] = entry;
 2291037        LinkMostRecent(slot);
 2291038        _count++;
 2291039        _cachedBytes = checked(_cachedBytes + entry.Payload.RetainedBytes);
 2291040    }
 1041
 1042    private bool TryIssueLeaseSlot(out int slot, out ulong generation)
 1043    {
 19021044        if (_freeLeaseCount > 0)
 1045        {
 18981046            slot = _freeLeaseSlots[--_freeLeaseCount];
 18981047            ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 1048            // RecycleLeaseSlot never returns retired generations to the free stack.
 18981049            leaseSlot.Generation++;
 18981050            generation = leaseSlot.Generation;
 18981051            return true;
 1052        }
 41053        slot = -1;
 41054        generation = 0;
 41055        return false;
 1056    }
 1057
 1058    private bool TryGetReservationSlot(
 1059        NavigationFlowFieldReservation reservation,
 1060        out int slot)
 1061    {
 5801062        slot = reservation.Slot;
 5801063        return ReferenceEquals(reservation.Owner, this)
 5801064            && (uint)slot < (uint)_leaseSlots.Length
 5801065            && _leaseSlots[slot].Generation == reservation.Generation
 5801066            && _leaseSlots[slot].State == LeaseSlotState.Reserved;
 1067    }
 1068
 1069    private void ReleaseReservationSlot(int slot)
 1070    {
 3201071        ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 3201072        _reservedPayloadBytes -= leaseSlot.MaximumBytes;
 3201073        _reservedLeaseCount--;
 3201074        RecycleLeaseSlot(slot);
 3201075    }
 1076
 1077    private void RecycleLeaseSlot(int slot)
 1078    {
 18951079        ref LeaseSlot leaseSlot = ref _leaseSlots[slot];
 18951080        leaseSlot.Entry = null;
 18951081        leaseSlot.MaximumBytes = 0;
 18951082        leaseSlot.State = RecycleLeaseSlotIdentity(
 18951083            leaseSlot.Generation,
 18951084            slot,
 18951085            _freeLeaseSlots,
 18951086            ref _freeLeaseCount);
 18951087    }
 1088
 1089    internal static LeaseSlotState RecycleLeaseSlotIdentity(
 1090        ulong generation,
 1091        int slot,
 1092        int[] freeLeaseSlots,
 1093        ref int freeLeaseCount)
 1094    {
 18971095        if (!NavigationGenerationCounter.CanAdvance(generation))
 11096            return LeaseSlotState.Retired;
 18961097        freeLeaseSlots[freeLeaseCount++] = slot;
 18961098        return LeaseSlotState.Free;
 1099    }
 1100
 1101    private void InvalidateExact(NavigationFlowFieldPayload payload)
 1102    {
 71103        lock (_sync)
 1104        {
 71105            FindSlot(payload.Key, out int slot, out bool found);
 71106            if (found && ReferenceEquals(_entries[slot]!.Payload, payload))
 51107                RemoveAt(slot, invalidate: true);
 461108            for (int i = 0; i < _leaseSlots.Length; i++)
 1109            {
 161110                ref LeaseSlot leaseSlot = ref _leaseSlots[i];
 161111                if ((leaseSlot.State is LeaseSlotState.ActivePayload
 161112                        or LeaseSlotState.ActiveGuide)
 161113                    && leaseSlot.Entry != null
 161114                    && ReferenceEquals(leaseSlot.Entry.Payload, payload))
 1115                {
 41116                    leaseSlot.Entry.IsInvalidated = true;
 1117                }
 1118            }
 71119        }
 71120    }
 1121
 1122    private void ResetUnderLock()
 1123    {
 14071124        while (_leastRecent >= 0)
 2061125            RemoveAt(_leastRecent, invalidate: true);
 12011126        Array.Clear(_states, 0, _states.Length);
 189121127        for (int i = 0; i < _leaseSlots.Length; i++)
 1128        {
 82551129            ref LeaseSlot leaseSlot = ref _leaseSlots[i];
 82551130            if (leaseSlot.State == LeaseSlotState.Reserved)
 1131            {
 31132                _reservedPayloadBytes -= leaseSlot.MaximumBytes;
 31133                _reservedLeaseCount--;
 31134                RecycleLeaseSlot(i);
 1135            }
 82521136            else if (leaseSlot.State is LeaseSlotState.ActivePayload
 82521137                or LeaseSlotState.ActiveGuide)
 1138            {
 91139                leaseSlot.Entry!.IsInvalidated = true;
 1140            }
 1141        }
 12011142    }
 1143
 1144    private void FindSlot(
 1145        NavigationFlowFieldPayloadKey key,
 1146        out int slot,
 1147        out bool found)
 1148    {
 19311149        slot = key.GetHashCode() & _mask;
 19311150        int firstRemoved = -1;
 39221151        for (int probe = 0; probe < _states.Length; probe++)
 1152        {
 19581153            byte state = _states[slot];
 19581154            if (state == 0)
 1155            {
 5081156                found = false;
 5081157                if (firstRemoved >= 0)
 161158                    slot = firstRemoved;
 5081159                return;
 1160            }
 14501161            if (state == 1 && _keys[slot].Equals(key))
 1162            {
 14201163                found = true;
 14201164                return;
 1165            }
 301166            if (state == 2 && firstRemoved < 0)
 191167                firstRemoved = slot;
 301168            slot = (slot + 1) & _mask;
 1169        }
 31170        found = false;
 31171        slot = firstRemoved;
 31172    }
 1173
 1174    private void Touch(int slot)
 1175    {
 13611176        if (_mostRecent == slot)
 13531177            return;
 81178        Unlink(slot);
 81179        LinkMostRecent(slot);
 81180    }
 1181
 1182    private void LinkMostRecent(int slot)
 1183    {
 2371184        _previous[slot] = _mostRecent;
 2371185        _next[slot] = -1;
 2371186        if (_mostRecent >= 0)
 211187            _next[_mostRecent] = slot;
 1188        else
 2161189            _leastRecent = slot;
 2371190        _mostRecent = slot;
 2371191    }
 1192
 1193    private void Unlink(int slot)
 1194    {
 2371195        int previous = _previous[slot];
 2371196        int next = _next[slot];
 2371197        if (previous >= 0)
 31198            _next[previous] = next;
 1199        else
 2341200            _leastRecent = next;
 2371201        if (next >= 0)
 201202            _previous[next] = previous;
 1203        else
 2171204            _mostRecent = previous;
 2371205        _previous[slot] = -1;
 2371206        _next[slot] = -1;
 2371207    }
 1208
 1209    private void RemoveAt(int slot, bool invalidate)
 1210    {
 2291211        CacheEntry entry = _entries[slot]!;
 2291212        _cachedBytes = checked(_cachedBytes - entry.Payload.RetainedBytes);
 2291213        entry.IsCached = false;
 2291214        entry.IsInvalidated = entry.IsInvalidated || invalidate;
 2291215        if (entry.LeaseCount != 0)
 171216            _detachedBytes = checked(_detachedBytes + entry.Payload.RetainedBytes);
 2291217        Unlink(slot);
 2291218        _keys[slot] = default;
 2291219        _entries[slot] = null;
 2291220        _states[slot] = 2;
 2291221        _count--;
 2291222    }
 1223
 1224    private sealed class CacheEntry
 1225    {
 2311226        internal CacheEntry(NavigationFlowFieldPayload payload, bool isCached)
 1227        {
 2311228            Payload = payload;
 2311229            IsCached = isCached;
 2311230        }
 1231
 1232        internal NavigationFlowFieldPayload Payload { get; }
 1233        internal int LeaseCount { get; set; }
 1234        internal bool IsCached { get; set; }
 1235        internal bool IsInvalidated { get; set; }
 1236    }
 1237
 1238    private struct LeaseSlot
 1239    {
 1240        internal CacheEntry? Entry;
 1241        internal long MaximumBytes;
 1242        internal ulong Generation;
 1243        internal LeaseSlotState State;
 1244    }
 1245}

Methods/Properties

.ctor(GridForge.Grids.GridWorld,System.Int32,System.Int64,System.Int64,System.Int64,System.Int32,System.Int32,Trailblazer.Pathing.NavigationImmediateRayWorkspace)
get_Count()
get_ImmediateRayWorkspace()
get_World()
get_CachedBytes()
get_LeasedBytes()
get_DetachedBytes()
get_ReservedPayloadBytes()
get_ActiveLeaseCount()
get_ReservedLeaseCount()
get_MaximumSinglePayloadBytes()
TryCreateGuide(Trailblazer.Pathing.NavigationWorldGraphStore,Trailblazer.Pathing.NavigationFlowQueryResult,Trailblazer.Pathing.NavigationFlowFieldLease&)
IsBoundGuideCurrent(Trailblazer.Pathing.NavigationFlowFieldStatus,System.Boolean)
ResolveBoundGuideValidation(System.Boolean,Trailblazer.Pathing.NavigationFlowFieldLease&)
TryReservePayload(System.Int64,Trailblazer.Pathing.NavigationFlowFieldReservation&)
ReleasePayloadReservation(Trailblazer.Pathing.NavigationFlowFieldReservation&)
TryCheckout(Trailblazer.Pathing.NavigationWorldGraphStore,Trailblazer.Pathing.NavigationWorldGraph,Trailblazer.Pathing.NavigationFlowFieldPayloadKey,Trailblazer.Pathing.NavigationCellAddress,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&,Trailblazer.Pathing.NavigationFlowFieldPayload&)
ResolveValidatedProof(Trailblazer.Pathing.NavigationFlowFieldStatus,Trailblazer.Pathing.NavigationFlowFieldPayload)
IsExactProofCurrent(Trailblazer.Pathing.NavigationWorldGraphStore,Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationCellAddress,Trailblazer.Pathing.NavigationFlowFieldStatus)
TryPublishOrPromote(Trailblazer.Pathing.NavigationWorldGraphStore,Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationCellAddress,Trailblazer.Pathing.NavigationFlowFieldReservation&,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&)
TryGetPayload(System.Int32,System.UInt64,Trailblazer.Pathing.NavigationFlowFieldPayload&)
TryGetGuidePayload(System.Int32,System.UInt64,Trailblazer.Pathing.NavigationFlowFieldPayload&)
Return(System.Int32,System.UInt64)
ReturnDetachedGuide(Trailblazer.Pathing.NavigationFlowFieldGuideLease,System.UInt64,System.Int32)
RetainGuideIfReusable(System.UInt64,System.Boolean,Trailblazer.Pathing.NavigationFlowFieldGuideLease,Trailblazer.Pathing.NavigationFlowFieldGuideLease[],System.Int32)
RemoveExact(Trailblazer.Pathing.NavigationFlowFieldPayload)
Reset()
Dispose()
TryFinishExisting(System.Int32,Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry,Trailblazer.Pathing.NavigationFlowFieldStatus,System.Int32,Trailblazer.Pathing.NavigationFlowFieldReservation&,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&)
PublishCandidate(Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldStatus,System.Int32,Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry,System.Int32,Trailblazer.Pathing.NavigationFlowFieldReservation&,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&)
TryCheckout(Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&)
ConvertReservation(Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry,System.Int32,Trailblazer.Pathing.NavigationFlowFieldReservation&)
ActivateEntry(Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry)
RentGuide(Trailblazer.Pathing.NavigationFlowFieldGuideLease[],System.Int32&,System.Int32)
CanActivate(Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry,System.Int32)
CanActivateNew(System.Int64,System.Int32)
ClassifyCoverage(Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationCellAddress)
ComparePrefixes(Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldPayload)
IsWorldCurrent(Trailblazer.Pathing.NavigationFlowFieldPayload)
IsPayloadCurrent(Trailblazer.Pathing.NavigationWorldGraph,Trailblazer.Pathing.NavigationFlowFieldPayload)
CompletePostLockValidation(Trailblazer.Pathing.NavigationWorldGraphStore,Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldStatus,Trailblazer.Pathing.NavigationFlowFieldPayloadLease&)
DependenciesAreEqual(Trailblazer.Pathing.GraphDependencyStamp,Trailblazer.Pathing.GraphDependencyStamp)
DependenciesAreSubset(Trailblazer.Pathing.GraphDependencyStamp,Trailblazer.Pathing.GraphDependencyStamp)
WorldDependencyIsSubset(Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldPayload)
NodeTransitionInstructionsAreEqual(Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldNode,Trailblazer.Pathing.NavigationFlowFieldPayload,Trailblazer.Pathing.NavigationFlowFieldNode)
TransitionInstructionsAreEqual(Trailblazer.Pathing.NavigationTransitionInstruction&,Trailblazer.Pathing.NavigationTransitionInstruction&)
ComponentDependenciesAreSubset(Trailblazer.Pathing.GraphComponentDependency[],Trailblazer.Pathing.GraphComponentDependency[])
PageDependenciesAreSubset(Trailblazer.Pathing.GraphPageDependency[],Trailblazer.Pathing.GraphPageDependency[])
ComparePageAddress(Trailblazer.Pathing.GraphPageDependency,Trailblazer.Pathing.GraphPageDependency)
EvictFor(System.Int64,System.Int32)
Insert(Trailblazer.Pathing.NavigationFlowFieldPayloadCache/CacheEntry)
TryIssueLeaseSlot(System.Int32&,System.UInt64&)
TryGetReservationSlot(Trailblazer.Pathing.NavigationFlowFieldReservation,System.Int32&)
ReleaseReservationSlot(System.Int32)
RecycleLeaseSlot(System.Int32)
RecycleLeaseSlotIdentity(System.UInt64,System.Int32,System.Int32[],System.Int32&)
InvalidateExact(Trailblazer.Pathing.NavigationFlowFieldPayload)
ResetUnderLock()
FindSlot(Trailblazer.Pathing.NavigationFlowFieldPayloadKey,System.Int32&,System.Boolean&)
Touch(System.Int32)
LinkMostRecent(System.Int32)
Unlink(System.Int32)
RemoveAt(System.Int32,System.Boolean)
.ctor(Trailblazer.Pathing.NavigationFlowFieldPayload,System.Boolean)