| | | 1 | | //======================================================================= |
| | | 2 | | // NavigationOperationCandidate.ExplicitConnections.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 | | |
| | | 8 | | using System; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using GridForge.Grids.Topology; |
| | | 11 | | |
| | | 12 | | namespace Trailblazer.Pathing; |
| | | 13 | | |
| | | 14 | | internal sealed partial class NavigationOperationCandidate |
| | | 15 | | { |
| | 22637 | 16 | | internal NavigationExplicitConnectionIndex ExplicitConnections => _explicitConnections; |
| | | 17 | | |
| | 37306 | 18 | | internal int ExplicitChangedSourceCount => _explicitChangedSources.Count; |
| | | 19 | | |
| | 19991 | 20 | | internal int ExplicitChangedOwnerCount => _explicitChangedOwners.Count; |
| | | 21 | | |
| | | 22 | | internal string GetExplicitChangedSourceAt(int ordinal) => |
| | 124 | 23 | | _explicitChangedSources.GetKeyAt(ordinal); |
| | | 24 | | |
| | | 25 | | internal NavigationConnectionOwnerKey GetExplicitChangedOwnerAt(int ordinal) => |
| | 17160 | 26 | | _explicitChangedOwners.GetAt(ordinal); |
| | | 27 | | |
| | | 28 | | internal ExplicitConnectionRefreshWork BeginExplicitConnectionRefresh( |
| | | 29 | | string mapId, |
| | | 30 | | NavigationExplicitConnectionIndex foldSource, |
| | | 31 | | GridCellPrism[] corridorPrisms, |
| | | 32 | | Vector3d[] corridorWaypoints, |
| | | 33 | | NavigationCellAddress[] corridorAddresses, |
| | 1650 | 34 | | NavigationAddressStampSet corridorAddressSet) => new( |
| | 1650 | 35 | | this, |
| | 1650 | 36 | | foldSource, |
| | 1650 | 37 | | mapId, |
| | 1650 | 38 | | transaction: null, |
| | 1650 | 39 | | corridorPrisms, |
| | 1650 | 40 | | corridorWaypoints, |
| | 1650 | 41 | | corridorAddresses, |
| | 1650 | 42 | | corridorAddressSet); |
| | | 43 | | |
| | | 44 | | internal ExplicitConnectionRefreshWork BeginExplicitConnectionRefresh( |
| | | 45 | | NavigationOverlayTransaction transaction, |
| | | 46 | | NavigationExplicitConnectionIndex foldSource, |
| | | 47 | | GridCellPrism[] corridorPrisms, |
| | | 48 | | Vector3d[] corridorWaypoints, |
| | | 49 | | NavigationCellAddress[] corridorAddresses, |
| | 292 | 50 | | NavigationAddressStampSet corridorAddressSet) => new( |
| | 292 | 51 | | this, |
| | 292 | 52 | | foldSource, |
| | 292 | 53 | | mapId: null, |
| | 292 | 54 | | transaction, |
| | 292 | 55 | | corridorPrisms, |
| | 292 | 56 | | corridorWaypoints, |
| | 292 | 57 | | corridorAddresses, |
| | 292 | 58 | | corridorAddressSet); |
| | | 59 | | |
| | | 60 | | private bool TrySelectConnection( |
| | | 61 | | NavigationConnectionOwnerKey owner, |
| | | 62 | | out MapState source, |
| | | 63 | | out NavigationConnection definition) |
| | | 64 | | { |
| | 4497 | 65 | | if (!_maps.TryGetValue(owner.MapId, out source!)) |
| | | 66 | | { |
| | 1 | 67 | | definition = null!; |
| | 1 | 68 | | return false; |
| | | 69 | | } |
| | 4496 | 70 | | if (source.Overlay.TryGetConnection( |
| | 4496 | 71 | | owner.ConnectionId, |
| | 4496 | 72 | | out NavigationConnectionOverlayOperation overlay)) |
| | | 73 | | { |
| | 4158 | 74 | | if (overlay.Kind == NavigationConnectionOverlayOperationKind.Upsert) |
| | | 75 | | { |
| | 4144 | 76 | | definition = overlay.Connection!; |
| | 4144 | 77 | | return true; |
| | | 78 | | } |
| | 14 | 79 | | if (overlay.Kind == NavigationConnectionOverlayOperationKind.Suppress) |
| | | 80 | | { |
| | 14 | 81 | | definition = null!; |
| | 14 | 82 | | return false; |
| | | 83 | | } |
| | | 84 | | } |
| | 338 | 85 | | ReadOnlySpan<NavigationConnection> baked = source.Map.ConnectionSpan; |
| | 338 | 86 | | int low = 0; |
| | 338 | 87 | | int high = baked.Length - 1; |
| | 864 | 88 | | while (low <= high) |
| | | 89 | | { |
| | 862 | 90 | | int middle = low + ((high - low) >> 1); |
| | 862 | 91 | | int comparison = string.CompareOrdinal(baked[middle].Id, owner.ConnectionId); |
| | 862 | 92 | | if (comparison == 0) |
| | | 93 | | { |
| | 336 | 94 | | definition = baked[middle]; |
| | 336 | 95 | | return true; |
| | | 96 | | } |
| | 526 | 97 | | if (comparison < 0) |
| | 283 | 98 | | low = middle + 1; |
| | | 99 | | else |
| | 243 | 100 | | high = middle - 1; |
| | | 101 | | } |
| | 2 | 102 | | definition = null!; |
| | 2 | 103 | | return false; |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | private bool TryGetSemanticPrism( |
| | | 107 | | NavigationCellAddress address, |
| | | 108 | | NavigationConnection connection, |
| | | 109 | | bool validateAnchor, |
| | | 110 | | Vector3d anchor, |
| | | 111 | | out GridCellPrism prism, |
| | | 112 | | out NavigationCell cell, |
| | | 113 | | out bool dormant) |
| | | 114 | | { |
| | 4709 | 115 | | dormant = false; |
| | 4709 | 116 | | if (!_maps.TryGetValue(address.MapId, out MapState target)) |
| | | 117 | | { |
| | 62 | 118 | | prism = default; |
| | 62 | 119 | | cell = default; |
| | 62 | 120 | | dormant = true; |
| | 62 | 121 | | return true; |
| | | 122 | | } |
| | 4647 | 123 | | if (!TryGetEffectiveCell(target, address.Index, out cell)) |
| | | 124 | | { |
| | 21 | 125 | | prism = default; |
| | 21 | 126 | | if (IsKnownSuppressed(target, address.Index)) |
| | | 127 | | { |
| | 17 | 128 | | dormant = true; |
| | 17 | 129 | | return true; |
| | | 130 | | } |
| | 4 | 131 | | return false; |
| | | 132 | | } |
| | 4626 | 133 | | if (connection.PortalRadiusClearance > cell.RadiusClearance |
| | 4626 | 134 | | || connection.PortalHeightClearance > cell.HeightClearance) |
| | | 135 | | { |
| | 2 | 136 | | prism = default; |
| | 2 | 137 | | return false; |
| | | 138 | | } |
| | 4624 | 139 | | bool hasPrism = target.Map.GridBinding.TryGetCellPrism(address.Index, out prism); |
| | | 140 | | System.Diagnostics.Debug.Assert(hasPrism); |
| | 4624 | 141 | | if (validateAnchor && !prism.Contains(anchor)) |
| | | 142 | | { |
| | 1 | 143 | | prism = default; |
| | 1 | 144 | | return false; |
| | | 145 | | } |
| | 4623 | 146 | | return true; |
| | | 147 | | } |
| | | 148 | | |
| | | 149 | | private static bool ValidateLowerBound( |
| | | 150 | | in GridCellPrism source, |
| | | 151 | | in GridCellPrism destination, |
| | | 152 | | NavigationCell destinationCell, |
| | | 153 | | NavigationConnection connection, |
| | | 154 | | Fixed64 corridorCost) |
| | | 155 | | { |
| | 3 | 156 | | Vector3d sourceFoot = new(source.Center.X, source.VerticalMin, source.Center.Z); |
| | 3 | 157 | | Vector3d destinationFoot = new( |
| | 3 | 158 | | destination.Center.X, |
| | 3 | 159 | | destination.VerticalMin, |
| | 3 | 160 | | destination.Center.Z); |
| | 3 | 161 | | bool hasApproach = Vector3d.TryGetDistance( |
| | 3 | 162 | | sourceFoot, |
| | 3 | 163 | | connection.EntryAnchor, |
| | 3 | 164 | | out Fixed64 approach); |
| | 3 | 165 | | bool hasDeparture = Vector3d.TryGetDistance( |
| | 3 | 166 | | connection.ExitAnchor, |
| | 3 | 167 | | destinationFoot, |
| | 3 | 168 | | out Fixed64 departure); |
| | | 169 | | System.Diagnostics.Debug.Assert(hasApproach && hasDeparture); |
| | 3 | 170 | | return Fixed64.TryAdd(approach, corridorCost, out Fixed64 total) |
| | 3 | 171 | | && Fixed64.TryAdd(total, departure, out total) |
| | 3 | 172 | | && Fixed64.TryAdd(total, connection.AdditionalCost, out total) |
| | 3 | 173 | | && Fixed64.TryAdd(total, destinationCell.EnterCost, out total) |
| | 3 | 174 | | && NavigationDistanceMath.TryCeiling( |
| | 3 | 175 | | sourceFoot, |
| | 3 | 176 | | destinationFoot, |
| | 3 | 177 | | out Fixed64 direct) |
| | 3 | 178 | | && total >= direct; |
| | | 179 | | } |
| | | 180 | | |
| | | 181 | | private static NavigationExplicitConnectionRecord Dormant( |
| | | 182 | | NavigationConnectionOwnerKey owner, |
| | 59 | 183 | | NavigationConnection connection) => new( |
| | 59 | 184 | | owner, |
| | 59 | 185 | | connection, |
| | 59 | 186 | | isActive: false, |
| | 59 | 187 | | Fixed64.Zero, |
| | 59 | 188 | | NavigationPagedSequence<GridNavigationPortal>.Empty); |
| | | 189 | | |
| | | 190 | | private static bool IsKnownSuppressed(MapState state, GridForge.Spatial.VoxelIndex index) => |
| | 26 | 191 | | state.Overlay.TryGetCell(index, out NavigationCellOverlayOperation operation) |
| | 26 | 192 | | && operation.Kind == NavigationCellOverlayOperationKind.Suppress; |
| | | 193 | | |
| | | 194 | | internal sealed class ExplicitConnectionRefreshWork |
| | | 195 | | { |
| | | 196 | | private const long BaseRetainedBytes = 792L; |
| | | 197 | | |
| | | 198 | | private readonly NavigationOperationCandidate _candidate; |
| | | 199 | | private readonly NavigationExplicitConnectionIndex _foldSource; |
| | | 200 | | private readonly string? _mapId; |
| | | 201 | | private readonly NavigationOverlayTransaction? _transaction; |
| | | 202 | | private readonly GridCellPrism[] _corridorPrisms; |
| | | 203 | | private readonly Vector3d[] _corridorWaypoints; |
| | | 204 | | private readonly NavigationCellAddress[] _corridorAddresses; |
| | | 205 | | private readonly NavigationAddressStampSet _corridorAddressSet; |
| | 1942 | 206 | | private PersistentStringMap<PersistentStringMap<bool>> _owners = |
| | 1942 | 207 | | PersistentStringMap<PersistentStringMap<bool>>.Empty; |
| | | 208 | | private int _stage; |
| | | 209 | | private int _mapIndex; |
| | | 210 | | private int _itemIndex; |
| | | 211 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Enumerator |
| | | 212 | | _incidentOwnerEnumerator; |
| | | 213 | | private NavigationConnectionOwnerKey _pendingIncidentOwner; |
| | | 214 | | private bool _incidentOwnerEnumerationStarted; |
| | | 215 | | private bool _hasPendingIncidentOwner; |
| | | 216 | | private int _compileMapIndex; |
| | | 217 | | private int _compileOwnerIndex; |
| | | 218 | | private NavigationConnectionOwnerKey _currentOwner; |
| | | 219 | | private MapState? _currentSource; |
| | | 220 | | private NavigationConnection? _currentDefinition; |
| | | 221 | | private NavigationExplicitConnectionRecord? _priorRecord; |
| | | 222 | | private NavigationExplicitConnectionRecord? _preparedRecord; |
| | | 223 | | private NavigationCell _destinationCell; |
| | | 224 | | private int _semanticIndex; |
| | | 225 | | private int _rawAddressIndex; |
| | | 226 | | private int _distinctAddressCount; |
| | | 227 | | private int _touchAddressIndex; |
| | | 228 | | private bool _currentInitialized; |
| | | 229 | | private bool _selectionCharged; |
| | | 230 | | private bool _isDormant; |
| | | 231 | | private bool _incidenceUnchanged; |
| | | 232 | | private bool _ownerUpdated; |
| | | 233 | | private bool _componentOwnerJournaled; |
| | | 234 | | private bool _corridorStarted; |
| | | 235 | | private GridNavigationCorridorValidationCursor _corridorCursor; |
| | | 236 | | private NavigationPagedSequence<GridNavigationPortal>.Builder? _portalBuilder; |
| | | 237 | | private int _portalCopyIndex; |
| | 1942 | 238 | | private PersistentStringMap<PersistentVoxelIndexMap<IncidenceRowDelta>> _rowDeltas = |
| | 1942 | 239 | | PersistentStringMap<PersistentVoxelIndexMap<IncidenceRowDelta>>.Empty; |
| | | 240 | | private IncidenceOwnerTree _finalOwners; |
| | | 241 | | private IncidenceOwnerTree.Node? _additionOwner; |
| | | 242 | | private NavigationConnection? _additionDefinition; |
| | | 243 | | private int _additionAddressIndex; |
| | | 244 | | private bool _additionOwnerInitialized; |
| | | 245 | | private int _rowMapIndex; |
| | | 246 | | private int _rowAddressIndex; |
| | | 247 | | private NavigationCellAddress _rowAddress; |
| | | 248 | | private IncidenceRowDelta? _rowDelta; |
| | | 249 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Enumerator _priorRowOwners; |
| | | 250 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Enumerator _additionRowOwners; |
| | | 251 | | private NavigationConnectionOwnerKey _priorRowOwner; |
| | | 252 | | private NavigationConnectionOwnerKey _additionRowOwner; |
| | | 253 | | private int _priorRowRemaining; |
| | | 254 | | private int _additionRowRemaining; |
| | | 255 | | private bool _priorRowOwnerReady; |
| | | 256 | | private bool _additionRowOwnerReady; |
| | | 257 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder? _finalRowBuilder; |
| | | 258 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder? _endpointRowBuilder; |
| | | 259 | | private NavigationConnectionOwnerKey _pendingEndpointOwner; |
| | | 260 | | private bool _endpointOwnerPending; |
| | | 261 | | private bool _incidentRowCommitted; |
| | | 262 | | private bool _rowInitialized; |
| | | 263 | | private long _rowDeltaInnerBytes; |
| | | 264 | | private int _rowDeltaInnerPages; |
| | | 265 | | private long _rowDeltaValueBytes; |
| | | 266 | | private int _rowDeltaValuePages; |
| | | 267 | | private long _innerOwnerBytes; |
| | | 268 | | private int _innerOwnerPages; |
| | | 269 | | private long _displacedSourcePayloadBytes; |
| | | 270 | | private int _displacedSourcePayloadPages; |
| | | 271 | | |
| | 1942 | 272 | | internal ExplicitConnectionRefreshWork( |
| | 1942 | 273 | | NavigationOperationCandidate candidate, |
| | 1942 | 274 | | NavigationExplicitConnectionIndex foldSource, |
| | 1942 | 275 | | string? mapId, |
| | 1942 | 276 | | NavigationOverlayTransaction? transaction, |
| | 1942 | 277 | | GridCellPrism[] corridorPrisms, |
| | 1942 | 278 | | Vector3d[] corridorWaypoints, |
| | 1942 | 279 | | NavigationCellAddress[] corridorAddresses, |
| | 1942 | 280 | | NavigationAddressStampSet corridorAddressSet) |
| | | 281 | | { |
| | 1942 | 282 | | _candidate = candidate; |
| | 1942 | 283 | | _foldSource = foldSource; |
| | 1942 | 284 | | _mapId = mapId; |
| | 1942 | 285 | | _transaction = transaction; |
| | 1942 | 286 | | _corridorPrisms = corridorPrisms; |
| | 1942 | 287 | | _corridorWaypoints = corridorWaypoints; |
| | 1942 | 288 | | _corridorAddresses = corridorAddresses; |
| | 1942 | 289 | | _corridorAddressSet = corridorAddressSet; |
| | 1942 | 290 | | } |
| | | 291 | | |
| | | 292 | | internal bool IsValid { get; private set; } = true; |
| | | 293 | | |
| | 2558 | 294 | | internal long DisplacedSourcePayloadBytes => _displacedSourcePayloadBytes; |
| | | 295 | | |
| | 2557 | 296 | | internal int DisplacedSourcePayloadPages => _displacedSourcePayloadPages; |
| | | 297 | | |
| | 2813 | 298 | | internal long RetainedBytes => checked( |
| | 2813 | 299 | | BaseRetainedBytes |
| | 2813 | 300 | | + _owners.RetainedBytes |
| | 2813 | 301 | | + _innerOwnerBytes |
| | 2813 | 302 | | + _rowDeltas.RetainedBytes |
| | 2813 | 303 | | + _rowDeltaInnerBytes |
| | 2813 | 304 | | + _rowDeltaValueBytes |
| | 2813 | 305 | | + _finalOwners.RetainedBytes |
| | 2813 | 306 | | + (_finalRowBuilder?.RetainedBytes ?? 0) |
| | 2813 | 307 | | + (_endpointRowBuilder?.RetainedBytes ?? 0) |
| | 2813 | 308 | | + (_portalBuilder?.RetainedBytes ?? 0) |
| | 2813 | 309 | | + (_ownerUpdated ? 0 : _preparedRecord?.RetainedBytes ?? 0)); |
| | | 310 | | |
| | 2556 | 311 | | internal int PersistentPageCount => checked( |
| | 2556 | 312 | | 1 |
| | 2556 | 313 | | + _owners.PersistentNodeCount |
| | 2556 | 314 | | + _innerOwnerPages |
| | 2556 | 315 | | + _rowDeltas.PersistentNodeCount |
| | 2556 | 316 | | + _rowDeltaInnerPages |
| | 2556 | 317 | | + _rowDeltaValuePages |
| | 2556 | 318 | | + _finalOwners.PersistentPageCount |
| | 2556 | 319 | | + (_finalRowBuilder?.PersistentPageCount ?? 0) |
| | 2556 | 320 | | + (_endpointRowBuilder?.PersistentPageCount ?? 0) |
| | 2556 | 321 | | + (_portalBuilder?.PersistentPageCount ?? 0) |
| | 2556 | 322 | | + (_ownerUpdated ? 0 : _preparedRecord?.PersistentPageCount ?? 0)); |
| | | 323 | | |
| | | 324 | | internal bool Advance(MaintenanceWorkMeter meter) |
| | | 325 | | { |
| | 2936 | 326 | | if (_stage < 4 && !AdvanceGather(meter)) |
| | 164 | 327 | | return false; |
| | 2996 | 328 | | while (_compileMapIndex < _owners.Count) |
| | | 329 | | { |
| | 723 | 330 | | PersistentStringMap<bool> map = _owners.GetValueAt(_compileMapIndex); |
| | 5203 | 331 | | while (_compileOwnerIndex < map.Count) |
| | | 332 | | { |
| | 4979 | 333 | | if (!_currentInitialized) |
| | 4497 | 334 | | InitializeCurrent(map); |
| | 4979 | 335 | | if (!AdvanceCompilation(meter)) |
| | 256 | 336 | | return false; |
| | 4723 | 337 | | if (!IsValid) |
| | 15 | 338 | | return true; |
| | 4708 | 339 | | if (!_componentOwnerJournaled |
| | 4708 | 340 | | && HasComponentEdgeChanged(_priorRecord, _preparedRecord)) |
| | | 341 | | { |
| | 4394 | 342 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 2 | 343 | | return false; |
| | 4392 | 344 | | _candidate._explicitChangedOwners = |
| | 4392 | 345 | | _candidate._explicitChangedOwners.Add(_currentOwner); |
| | 4392 | 346 | | _componentOwnerJournaled = true; |
| | | 347 | | } |
| | 4706 | 348 | | _incidenceUnchanged = _priorRecord != null |
| | 4706 | 349 | | && _preparedRecord != null |
| | 4706 | 350 | | && ReferenceEquals( |
| | 4706 | 351 | | _priorRecord.Definition, |
| | 4706 | 352 | | _preparedRecord.Definition); |
| | 4706 | 353 | | if (_incidenceUnchanged) |
| | 50 | 354 | | UpdateOwner(); |
| | 4706 | 355 | | if (!_ownerUpdated) |
| | | 356 | | { |
| | 4611 | 357 | | if (!AdvanceDistinctAddresses(_priorRecord?.Definition, meter)) |
| | 91 | 358 | | return false; |
| | 4520 | 359 | | if (!AdvanceOldIncidenceTouches(meter)) |
| | 89 | 360 | | return false; |
| | 4431 | 361 | | UpdateOwner(); |
| | | 362 | | } |
| | 4526 | 363 | | if (!_incidenceUnchanged) |
| | | 364 | | { |
| | 4476 | 365 | | if (_preparedRecord != null) |
| | | 366 | | { |
| | 4460 | 367 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 46 | 368 | | return false; |
| | 4414 | 369 | | _finalOwners.Add( |
| | 4414 | 370 | | _currentOwner, |
| | 4414 | 371 | | _candidate._explicitConnections); |
| | | 372 | | } |
| | | 373 | | } |
| | 4480 | 374 | | _compileOwnerIndex++; |
| | 4480 | 375 | | ResetCurrent(); |
| | | 376 | | } |
| | 224 | 377 | | _compileMapIndex++; |
| | 224 | 378 | | _compileOwnerIndex = 0; |
| | | 379 | | } |
| | 2273 | 380 | | if (!AdvanceFinalAdditions(meter)) |
| | 212 | 381 | | return false; |
| | 2061 | 382 | | return AdvanceIncidenceRows(meter); |
| | | 383 | | } |
| | | 384 | | |
| | | 385 | | private void InitializeCurrent(PersistentStringMap<bool> map) |
| | | 386 | | { |
| | 4497 | 387 | | _corridorAddressSet.Reset(); |
| | 4497 | 388 | | _currentOwner = new NavigationConnectionOwnerKey( |
| | 4497 | 389 | | _owners.GetKeyAt(_compileMapIndex), |
| | 4497 | 390 | | map.GetKeyAt(_compileOwnerIndex)); |
| | 4497 | 391 | | if (_candidate.TrySelectConnection( |
| | 4497 | 392 | | _currentOwner, |
| | 4497 | 393 | | out MapState source, |
| | 4497 | 394 | | out NavigationConnection definition)) |
| | | 395 | | { |
| | 4480 | 396 | | _currentSource = source; |
| | 4480 | 397 | | _currentDefinition = definition; |
| | | 398 | | } |
| | 4497 | 399 | | _candidate._explicitConnections.TryGet(_currentOwner, out _priorRecord!); |
| | 4497 | 400 | | _currentInitialized = true; |
| | 4497 | 401 | | } |
| | | 402 | | |
| | | 403 | | private bool AdvanceCompilation(MaintenanceWorkMeter meter) |
| | | 404 | | { |
| | 4979 | 405 | | if (_preparedRecord != null) |
| | 212 | 406 | | return true; |
| | 4767 | 407 | | if (_currentDefinition == null) |
| | | 408 | | { |
| | 32 | 409 | | if (_selectionCharged) |
| | 14 | 410 | | return true; |
| | 18 | 411 | | if (!meter.TryConsumeExplicitEdges(1)) |
| | 1 | 412 | | return false; |
| | 17 | 413 | | _selectionCharged = true; |
| | 17 | 414 | | return true; |
| | | 415 | | } |
| | | 416 | | |
| | 4735 | 417 | | int semanticCount = _currentDefinition.Witnesses.Count + 2; |
| | | 418 | | System.Diagnostics.Debug.Assert( |
| | | 419 | | _corridorWaypoints.Length / 2 == _corridorPrisms.Length - 1); |
| | | 420 | | System.Diagnostics.Debug.Assert(semanticCount <= _corridorPrisms.Length); |
| | 13913 | 421 | | while (_semanticIndex < semanticCount) |
| | | 422 | | { |
| | 9288 | 423 | | if (!meter.TryConsumeExplicitEdges(1)) |
| | 99 | 424 | | return false; |
| | 9189 | 425 | | if (!CaptureSemanticPrism(_semanticIndex)) |
| | | 426 | | { |
| | 11 | 427 | | IsValid = false; |
| | 11 | 428 | | return true; |
| | | 429 | | } |
| | 9178 | 430 | | _semanticIndex++; |
| | | 431 | | } |
| | 4625 | 432 | | if (_isDormant) |
| | | 433 | | { |
| | 59 | 434 | | _preparedRecord = Dormant(_currentOwner, _currentDefinition); |
| | 59 | 435 | | return true; |
| | | 436 | | } |
| | | 437 | | |
| | 4566 | 438 | | int prismCount = semanticCount; |
| | 4566 | 439 | | if (!_corridorStarted) |
| | | 440 | | { |
| | 4410 | 441 | | _corridorCursor = new GridNavigationCorridorValidationCursor( |
| | 4410 | 442 | | prismCount, |
| | 4410 | 443 | | _currentDefinition.EntryAnchor, |
| | 4410 | 444 | | _currentDefinition.ExitAnchor, |
| | 4410 | 445 | | _currentDefinition.PortalRadiusClearance, |
| | 4410 | 446 | | _currentDefinition.PortalHeightClearance); |
| | 4410 | 447 | | _corridorStarted = true; |
| | | 448 | | } |
| | 26911 | 449 | | while (_corridorCursor.Status == GridNavigationCorridorValidationStatus.InProgress) |
| | | 450 | | { |
| | 22501 | 451 | | if (!meter.TryConsumeExplicitEdges(1)) |
| | 156 | 452 | | return false; |
| | 22345 | 453 | | _corridorCursor.Advance( |
| | 22345 | 454 | | _corridorPrisms.AsSpan(0, prismCount), |
| | 22345 | 455 | | _corridorWaypoints.AsSpan(0, (prismCount - 1) * 2), |
| | 22345 | 456 | | maxWork: 1); |
| | 22345 | 457 | | if (_corridorCursor.TryGetCurrentPortal(out GridNavigationPortal portal)) |
| | | 458 | | { |
| | 4556 | 459 | | _portalBuilder ??= new NavigationPagedSequence<GridNavigationPortal>.Builder( |
| | 4556 | 460 | | elementBytes: GridNavigationPortal.SizeInBytes); |
| | 4556 | 461 | | _portalBuilder.Append(portal); |
| | 4556 | 462 | | _portalCopyIndex++; |
| | | 463 | | } |
| | | 464 | | } |
| | 4410 | 465 | | if (_corridorCursor.Status != GridNavigationCorridorValidationStatus.Complete) |
| | | 466 | | { |
| | 2 | 467 | | IsValid = false; |
| | 2 | 468 | | return true; |
| | | 469 | | } |
| | | 470 | | System.Diagnostics.Debug.Assert(_portalCopyIndex == prismCount - 1); |
| | 4408 | 471 | | if (_currentDefinition.IsLowerBoundCertified |
| | 4408 | 472 | | && !ValidateLowerBound( |
| | 4408 | 473 | | _corridorPrisms[0], |
| | 4408 | 474 | | _corridorPrisms[prismCount - 1], |
| | 4408 | 475 | | _destinationCell, |
| | 4408 | 476 | | _currentDefinition, |
| | 4408 | 477 | | _corridorCursor.GeometricCost)) |
| | | 478 | | { |
| | 2 | 479 | | IsValid = false; |
| | 2 | 480 | | return true; |
| | | 481 | | } |
| | | 482 | | // Every valid connection corridor contains source and destination prisms, |
| | | 483 | | // so successful validation always emits at least one portal. |
| | | 484 | | System.Diagnostics.Debug.Assert(_portalBuilder != null); |
| | 4406 | 485 | | NavigationPagedSequence<GridNavigationPortal> portals = _portalBuilder!.Seal(); |
| | 4406 | 486 | | _portalBuilder = null; |
| | 4406 | 487 | | _preparedRecord = new NavigationExplicitConnectionRecord( |
| | 4406 | 488 | | _currentOwner, |
| | 4406 | 489 | | _currentDefinition, |
| | 4406 | 490 | | isActive: true, |
| | 4406 | 491 | | _corridorCursor.GeometricCost, |
| | 4406 | 492 | | portals, |
| | 4406 | 493 | | isLowerBoundCertified: _currentDefinition.IsLowerBoundCertified); |
| | 4406 | 494 | | return true; |
| | | 495 | | } |
| | | 496 | | |
| | | 497 | | private bool CaptureSemanticPrism(int semanticIndex) |
| | | 498 | | { |
| | 9189 | 499 | | NavigationConnection connection = _currentDefinition!; |
| | 9189 | 500 | | if (semanticIndex == 0) |
| | | 501 | | { |
| | 4480 | 502 | | if (!TryGetEffectiveCell( |
| | 4480 | 503 | | _currentSource!, |
| | 4480 | 504 | | connection.SourceIndex, |
| | 4480 | 505 | | out NavigationCell sourceCell)) |
| | | 506 | | { |
| | 5 | 507 | | if (IsKnownSuppressed(_currentSource!, connection.SourceIndex)) |
| | | 508 | | { |
| | 4 | 509 | | _isDormant = true; |
| | 4 | 510 | | _corridorPrisms[0] = default; |
| | 4 | 511 | | return true; |
| | | 512 | | } |
| | 1 | 513 | | return false; |
| | | 514 | | } |
| | 4475 | 515 | | if (connection.PortalRadiusClearance > sourceCell.RadiusClearance |
| | 4475 | 516 | | || connection.PortalHeightClearance > sourceCell.HeightClearance) |
| | | 517 | | { |
| | 2 | 518 | | return false; |
| | | 519 | | } |
| | 4473 | 520 | | bool hasPrism = _currentSource!.Map.GridBinding.TryGetCellPrism( |
| | 4473 | 521 | | connection.SourceIndex, |
| | 4473 | 522 | | out _corridorPrisms[0]); |
| | | 523 | | System.Diagnostics.Debug.Assert(hasPrism); |
| | 4473 | 524 | | if (!_corridorPrisms[0].Contains(connection.EntryAnchor)) |
| | 1 | 525 | | return false; |
| | 4472 | 526 | | return true; |
| | | 527 | | } |
| | | 528 | | |
| | 4709 | 529 | | bool isDestination = semanticIndex == connection.Witnesses.Count + 1; |
| | 4709 | 530 | | NavigationCellAddress address = isDestination |
| | 4709 | 531 | | ? connection.Destination |
| | 4709 | 532 | | : connection.Witnesses[semanticIndex - 1]; |
| | 4709 | 533 | | if (!_candidate.TryGetSemanticPrism( |
| | 4709 | 534 | | address, |
| | 4709 | 535 | | connection, |
| | 4709 | 536 | | validateAnchor: isDestination, |
| | 4709 | 537 | | isDestination ? connection.ExitAnchor : default, |
| | 4709 | 538 | | out _corridorPrisms[semanticIndex], |
| | 4709 | 539 | | out NavigationCell cell, |
| | 4709 | 540 | | out bool dormant)) |
| | | 541 | | { |
| | 7 | 542 | | return false; |
| | | 543 | | } |
| | 4702 | 544 | | _isDormant |= dormant; |
| | 4702 | 545 | | if (isDestination && !dormant) |
| | 4428 | 546 | | _destinationCell = cell; |
| | 4702 | 547 | | return true; |
| | | 548 | | } |
| | | 549 | | |
| | | 550 | | private bool AdvanceDistinctAddresses( |
| | | 551 | | NavigationConnection? definition, |
| | | 552 | | MaintenanceWorkMeter meter) |
| | | 553 | | { |
| | 9236 | 554 | | if (definition == null) |
| | 4344 | 555 | | return true; |
| | 4892 | 556 | | int rawCount = definition.Witnesses.Count + 2; |
| | 14083 | 557 | | while (_rawAddressIndex < rawCount) |
| | | 558 | | { |
| | 9395 | 559 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 204 | 560 | | return false; |
| | 9191 | 561 | | NavigationCellAddress address = GetRawAddress( |
| | 9191 | 562 | | _currentOwner, |
| | 9191 | 563 | | definition, |
| | 9191 | 564 | | _rawAddressIndex++); |
| | 9191 | 565 | | if (_corridorAddressSet.Add(address)) |
| | 9190 | 566 | | _corridorAddresses[_distinctAddressCount++] = address; |
| | | 567 | | } |
| | 4688 | 568 | | return true; |
| | | 569 | | } |
| | | 570 | | |
| | | 571 | | private bool AdvanceOldIncidenceTouches(MaintenanceWorkMeter meter) |
| | | 572 | | { |
| | 4520 | 573 | | if (_priorRecord == null) |
| | 4344 | 574 | | return true; |
| | 372 | 575 | | while (_touchAddressIndex < _distinctAddressCount) |
| | | 576 | | { |
| | 285 | 577 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 89 | 578 | | return false; |
| | 196 | 579 | | NavigationCellAddress address = _corridorAddresses[_touchAddressIndex++]; |
| | 196 | 580 | | IncidenceRowDelta row = GetOrCreateRowDelta(address); |
| | 196 | 581 | | if (IsEndpoint(address, _currentOwner, _priorRecord.Definition)) |
| | 174 | 582 | | row.MarkEndpointTouched(); |
| | | 583 | | } |
| | 87 | 584 | | return true; |
| | | 585 | | } |
| | | 586 | | |
| | | 587 | | private void UpdateOwner() |
| | | 588 | | { |
| | 4481 | 589 | | if (_preparedRecord != null) |
| | | 590 | | { |
| | 4465 | 591 | | _candidate.RecordExplicitRecordOwnership( |
| | 4465 | 592 | | _currentOwner, |
| | 4465 | 593 | | _preparedRecord, |
| | 4465 | 594 | | _foldSource, |
| | 4465 | 595 | | ref _displacedSourcePayloadBytes, |
| | 4465 | 596 | | ref _displacedSourcePayloadPages); |
| | 4465 | 597 | | _candidate._explicitConnections = _candidate._explicitConnections.SetOwner( |
| | 4465 | 598 | | _preparedRecord, |
| | 4465 | 599 | | out int copiedNodes); |
| | 4465 | 600 | | _candidate.RecordPersistentCopies(copiedNodes); |
| | | 601 | | } |
| | | 602 | | else |
| | | 603 | | { |
| | 16 | 604 | | _candidate.RecordExplicitRecordOwnership( |
| | 16 | 605 | | _currentOwner, |
| | 16 | 606 | | next: null, |
| | 16 | 607 | | _foldSource, |
| | 16 | 608 | | ref _displacedSourcePayloadBytes, |
| | 16 | 609 | | ref _displacedSourcePayloadPages); |
| | 16 | 610 | | _candidate._explicitConnections = _candidate._explicitConnections.RemoveOwner( |
| | 16 | 611 | | _currentOwner, |
| | 16 | 612 | | out _, |
| | 16 | 613 | | out int copiedNodes); |
| | 16 | 614 | | _candidate.RecordPersistentCopies(copiedNodes); |
| | | 615 | | } |
| | 4481 | 616 | | _ownerUpdated = true; |
| | 4481 | 617 | | } |
| | | 618 | | |
| | | 619 | | private bool AdvanceFinalAdditions(MaintenanceWorkMeter meter) |
| | | 620 | | { |
| | 4413 | 621 | | while (true) |
| | | 622 | | { |
| | 6686 | 623 | | if (!_additionOwnerInitialized) |
| | | 624 | | { |
| | 6475 | 625 | | _additionOwner = _finalOwners.GetSuccessor(_additionOwner); |
| | 6475 | 626 | | if (_additionOwner == null) |
| | | 627 | | { |
| | 2061 | 628 | | _finalOwners.Clear(); |
| | 2061 | 629 | | return true; |
| | | 630 | | } |
| | 4414 | 631 | | _currentOwner = _additionOwner.Owner; |
| | 4414 | 632 | | _candidate._explicitConnections.TryGet( |
| | 4414 | 633 | | _currentOwner, |
| | 4414 | 634 | | out NavigationExplicitConnectionRecord record); |
| | 4414 | 635 | | _additionDefinition = record.Definition; |
| | 4414 | 636 | | _corridorAddressSet.Reset(); |
| | 4414 | 637 | | _rawAddressIndex = 0; |
| | 4414 | 638 | | _distinctAddressCount = 0; |
| | 4414 | 639 | | _additionAddressIndex = 0; |
| | 4414 | 640 | | _additionOwnerInitialized = true; |
| | | 641 | | } |
| | 4625 | 642 | | if (!AdvanceDistinctAddresses(_additionDefinition, meter)) |
| | 113 | 643 | | return false; |
| | 13506 | 644 | | while (_additionAddressIndex < _distinctAddressCount) |
| | | 645 | | { |
| | 9093 | 646 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 99 | 647 | | return false; |
| | 8994 | 648 | | IncidenceRowDelta row = GetOrCreateRowDelta( |
| | 8994 | 649 | | _corridorAddresses[_additionAddressIndex++]); |
| | 8994 | 650 | | long priorBytes = row.RetainedBytes; |
| | 8994 | 651 | | int priorPages = row.PersistentPageCount; |
| | 8994 | 652 | | row.AppendAddition(_currentOwner); |
| | 8994 | 653 | | NavigationCellAddress address = |
| | 8994 | 654 | | _corridorAddresses[_additionAddressIndex - 1]; |
| | 8994 | 655 | | if (IsEndpoint(address, _currentOwner, _additionDefinition!)) |
| | 8826 | 656 | | row.MarkEndpointTouched(); |
| | 8994 | 657 | | _rowDeltaValueBytes = checked( |
| | 8994 | 658 | | _rowDeltaValueBytes - priorBytes + row.RetainedBytes); |
| | 8994 | 659 | | _rowDeltaValuePages = checked( |
| | 8994 | 660 | | _rowDeltaValuePages - priorPages + row.PersistentPageCount); |
| | | 661 | | } |
| | 4413 | 662 | | _additionOwnerInitialized = false; |
| | | 663 | | } |
| | | 664 | | } |
| | | 665 | | |
| | | 666 | | private bool AdvanceIncidenceRows(MaintenanceWorkMeter meter) |
| | | 667 | | { |
| | 2410 | 668 | | while (_rowMapIndex < _rowDeltas.Count) |
| | | 669 | | { |
| | 743 | 670 | | PersistentVoxelIndexMap<IncidenceRowDelta> map = |
| | 743 | 671 | | _rowDeltas.GetValueAt(_rowMapIndex); |
| | 5321 | 672 | | while (_rowAddressIndex < map.Count) |
| | | 673 | | { |
| | 4972 | 674 | | if (!_rowInitialized) |
| | 4578 | 675 | | InitializeRow(map); |
| | 4972 | 676 | | if (!AdvancePriorRowFilter(meter)) |
| | 86 | 677 | | return false; |
| | 4886 | 678 | | if (!AdvanceRowMerge(meter)) |
| | 222 | 679 | | return false; |
| | 4664 | 680 | | if (!_incidentRowCommitted) |
| | | 681 | | { |
| | 4623 | 682 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 45 | 683 | | return false; |
| | 4578 | 684 | | NavigationPagedSequence<NavigationConnectionOwnerKey> next = |
| | 4578 | 685 | | _finalRowBuilder?.Seal() |
| | 4578 | 686 | | ?? NavigationPagedSequence<NavigationConnectionOwnerKey>.Empty; |
| | 4578 | 687 | | _finalRowBuilder = null; |
| | 4578 | 688 | | NavigationPagedSequence<NavigationConnectionOwnerKey> prior = |
| | 4578 | 689 | | _candidate._explicitConnections.GetIncidentOwnerRow(_rowAddress); |
| | 4578 | 690 | | _candidate.RecordExplicitIncidenceOwnership( |
| | 4578 | 691 | | _rowAddress, |
| | 4578 | 692 | | next, |
| | 4578 | 693 | | _foldSource, |
| | 4578 | 694 | | ref _displacedSourcePayloadBytes, |
| | 4578 | 695 | | ref _displacedSourcePayloadPages); |
| | 4578 | 696 | | _candidate._explicitConnections = |
| | 4578 | 697 | | _candidate._explicitConnections.SetIncidentRow( |
| | 4578 | 698 | | _rowAddress, |
| | 4578 | 699 | | prior, |
| | 4578 | 700 | | next, |
| | 4578 | 701 | | out int copiedNodes); |
| | 4578 | 702 | | _candidate.RecordPersistentCopies(copiedNodes); |
| | 4578 | 703 | | _incidentRowCommitted = true; |
| | | 704 | | } |
| | 4619 | 705 | | if (_rowDelta!.EndpointTouched) |
| | | 706 | | { |
| | 4499 | 707 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 41 | 708 | | return false; |
| | 4458 | 709 | | NavigationPagedSequence<NavigationConnectionOwnerKey> next = |
| | 4458 | 710 | | _endpointRowBuilder?.Seal() |
| | 4458 | 711 | | ?? NavigationPagedSequence<NavigationConnectionOwnerKey>.Empty; |
| | 4458 | 712 | | _endpointRowBuilder = null; |
| | 4458 | 713 | | NavigationPagedSequence<NavigationConnectionOwnerKey> prior = |
| | 4458 | 714 | | _candidate._explicitConnections.GetEndpointOwnerRow(_rowAddress); |
| | 4458 | 715 | | _candidate.RecordExplicitEndpointOwnership( |
| | 4458 | 716 | | _rowAddress, |
| | 4458 | 717 | | next, |
| | 4458 | 718 | | _foldSource, |
| | 4458 | 719 | | ref _displacedSourcePayloadBytes, |
| | 4458 | 720 | | ref _displacedSourcePayloadPages); |
| | 4458 | 721 | | _candidate._explicitConnections = |
| | 4458 | 722 | | _candidate._explicitConnections.SetEndpointRow( |
| | 4458 | 723 | | _rowAddress, |
| | 4458 | 724 | | prior, |
| | 4458 | 725 | | next, |
| | 4458 | 726 | | out int copiedNodes); |
| | 4458 | 727 | | _candidate.RecordPersistentCopies(copiedNodes); |
| | | 728 | | } |
| | 4578 | 729 | | ReleaseCurrentRow(); |
| | 4578 | 730 | | _rowAddressIndex++; |
| | | 731 | | } |
| | 349 | 732 | | _rowMapIndex++; |
| | 349 | 733 | | _rowAddressIndex = 0; |
| | | 734 | | } |
| | 1667 | 735 | | _rowDeltas = |
| | 1667 | 736 | | PersistentStringMap<PersistentVoxelIndexMap<IncidenceRowDelta>>.Empty; |
| | 1667 | 737 | | _rowDeltaInnerBytes = 0; |
| | 1667 | 738 | | _rowDeltaInnerPages = 0; |
| | 1667 | 739 | | _rowDeltaValueBytes = 0; |
| | 1667 | 740 | | _rowDeltaValuePages = 0; |
| | 1667 | 741 | | return true; |
| | | 742 | | } |
| | | 743 | | |
| | | 744 | | private void InitializeRow(PersistentVoxelIndexMap<IncidenceRowDelta> map) |
| | | 745 | | { |
| | 4578 | 746 | | _rowAddress = new NavigationCellAddress( |
| | 4578 | 747 | | _rowDeltas.GetKeyAt(_rowMapIndex), |
| | 4578 | 748 | | map.GetKeyAt(_rowAddressIndex)); |
| | 4578 | 749 | | _rowDelta = map.GetValueAt(_rowAddressIndex); |
| | 4578 | 750 | | long priorBytes = _rowDelta.RetainedBytes; |
| | 4578 | 751 | | int priorPages = _rowDelta.PersistentPageCount; |
| | 4578 | 752 | | NavigationPagedSequence<NavigationConnectionOwnerKey> additions = |
| | 4578 | 753 | | _rowDelta.SealAdditions(); |
| | 4578 | 754 | | _rowDeltaValueBytes = checked( |
| | 4578 | 755 | | _rowDeltaValueBytes - priorBytes + _rowDelta.RetainedBytes); |
| | 4578 | 756 | | _rowDeltaValuePages = checked( |
| | 4578 | 757 | | _rowDeltaValuePages - priorPages + _rowDelta.PersistentPageCount); |
| | 4578 | 758 | | NavigationPagedSequence<NavigationConnectionOwnerKey> prior = |
| | 4578 | 759 | | _candidate._explicitConnections.GetIncidentOwnerRow(_rowAddress); |
| | 4578 | 760 | | _priorRowOwners = prior.GetEnumerator(); |
| | 4578 | 761 | | _priorRowRemaining = prior.Count; |
| | 4578 | 762 | | _additionRowOwners = additions.GetEnumerator(); |
| | 4578 | 763 | | _additionRowRemaining = additions.Count; |
| | 4578 | 764 | | _rowInitialized = true; |
| | 4578 | 765 | | } |
| | | 766 | | |
| | | 767 | | private bool AdvancePriorRowFilter(MaintenanceWorkMeter meter) |
| | | 768 | | { |
| | 14568 | 769 | | while (!_priorRowOwnerReady && _priorRowRemaining != 0) |
| | | 770 | | { |
| | 464 | 771 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 92 | 772 | | return false; |
| | 372 | 773 | | _priorRowOwners.MoveNext(); |
| | 372 | 774 | | _priorRowOwner = _priorRowOwners.Current; |
| | 372 | 775 | | _priorRowRemaining--; |
| | 372 | 776 | | if (!IsIncidenceChanged(_priorRowOwner)) |
| | 180 | 777 | | _priorRowOwnerReady = true; |
| | | 778 | | } |
| | 14104 | 779 | | return true; |
| | | 780 | | } |
| | | 781 | | |
| | | 782 | | private bool AdvanceRowMerge(MaintenanceWorkMeter meter) |
| | | 783 | | { |
| | 22929 | 784 | | while (_priorRowOwnerReady || _priorRowRemaining != 0 |
| | 22929 | 785 | | || _additionRowOwnerReady || _additionRowRemaining != 0 |
| | 22929 | 786 | | || _endpointOwnerPending) |
| | | 787 | | { |
| | 18267 | 788 | | if (_endpointOwnerPending) |
| | | 789 | | { |
| | 8962 | 790 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 93 | 791 | | return false; |
| | 8869 | 792 | | _endpointRowBuilder ??= |
| | 8869 | 793 | | new NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder(16); |
| | 8869 | 794 | | _endpointRowBuilder.Append(_pendingEndpointOwner); |
| | 8869 | 795 | | _endpointOwnerPending = false; |
| | 8869 | 796 | | continue; |
| | | 797 | | } |
| | 9305 | 798 | | if (!_priorRowOwnerReady && !AdvancePriorRowFilter(meter)) |
| | 6 | 799 | | return false; |
| | 9299 | 800 | | if (!_additionRowOwnerReady && _additionRowRemaining != 0) |
| | | 801 | | { |
| | 8994 | 802 | | _additionRowOwners.MoveNext(); |
| | 8994 | 803 | | _additionRowOwner = _additionRowOwners.Current; |
| | 8994 | 804 | | _additionRowRemaining--; |
| | 8994 | 805 | | _additionRowOwnerReady = true; |
| | | 806 | | } |
| | 9299 | 807 | | if (!_priorRowOwnerReady && !_additionRowOwnerReady) |
| | 2 | 808 | | return true; |
| | 9297 | 809 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 123 | 810 | | return false; |
| | | 811 | | NavigationConnectionOwnerKey next; |
| | 9174 | 812 | | if (!_priorRowOwnerReady) |
| | | 813 | | { |
| | 8988 | 814 | | next = _additionRowOwner; |
| | 8988 | 815 | | _additionRowOwnerReady = false; |
| | | 816 | | } |
| | 186 | 817 | | else if (!_additionRowOwnerReady) |
| | | 818 | | { |
| | 9 | 819 | | next = _priorRowOwner; |
| | 9 | 820 | | _priorRowOwnerReady = false; |
| | | 821 | | } |
| | 177 | 822 | | else if (_candidate._explicitConnections.CompareOwners( |
| | 177 | 823 | | _priorRowOwner, |
| | 177 | 824 | | _additionRowOwner) <= 0) |
| | | 825 | | { |
| | 171 | 826 | | next = _priorRowOwner; |
| | 171 | 827 | | _priorRowOwnerReady = false; |
| | | 828 | | } |
| | | 829 | | else |
| | | 830 | | { |
| | 6 | 831 | | next = _additionRowOwner; |
| | 6 | 832 | | _additionRowOwnerReady = false; |
| | | 833 | | } |
| | 9174 | 834 | | _finalRowBuilder ??= |
| | 9174 | 835 | | new NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder(16); |
| | 9174 | 836 | | _finalRowBuilder.Append(next); |
| | 9174 | 837 | | bool hasFinalRecord = _candidate._explicitConnections.TryGet( |
| | 9174 | 838 | | next, |
| | 9174 | 839 | | out NavigationExplicitConnectionRecord record); |
| | | 840 | | System.Diagnostics.Debug.Assert(hasFinalRecord); |
| | 9174 | 841 | | if (_rowDelta!.EndpointTouched |
| | 9174 | 842 | | && IsEndpoint(_rowAddress, next, record.Definition)) |
| | | 843 | | { |
| | 8869 | 844 | | _pendingEndpointOwner = next; |
| | 8869 | 845 | | _endpointOwnerPending = true; |
| | | 846 | | } |
| | | 847 | | } |
| | 4662 | 848 | | return true; |
| | | 849 | | } |
| | | 850 | | |
| | | 851 | | private bool IsIncidenceChanged(NavigationConnectionOwnerKey owner) |
| | | 852 | | { |
| | 372 | 853 | | if (!_owners.TryGetValue(owner.MapId, out PersistentStringMap<bool> map) |
| | 372 | 854 | | || !map.ContainsKey(owner.ConnectionId)) |
| | | 855 | | { |
| | 171 | 856 | | return false; |
| | | 857 | | } |
| | 201 | 858 | | bool hadPrior = _foldSource.TryGet( |
| | 201 | 859 | | owner, |
| | 201 | 860 | | out NavigationExplicitConnectionRecord prior); |
| | 201 | 861 | | bool hasFinal = _candidate._explicitConnections.TryGet( |
| | 201 | 862 | | owner, |
| | 201 | 863 | | out NavigationExplicitConnectionRecord final); |
| | | 864 | | System.Diagnostics.Debug.Assert(hadPrior); |
| | 201 | 865 | | return !hasFinal || !ReferenceEquals(prior.Definition, final.Definition); |
| | | 866 | | } |
| | | 867 | | |
| | | 868 | | private IncidenceRowDelta GetOrCreateRowDelta(NavigationCellAddress address) |
| | | 869 | | { |
| | 9190 | 870 | | bool hadMap = _rowDeltas.TryGetValue( |
| | 9190 | 871 | | address.MapId, |
| | 9190 | 872 | | out PersistentVoxelIndexMap<IncidenceRowDelta> existing); |
| | 9190 | 873 | | PersistentVoxelIndexMap<IncidenceRowDelta> map = hadMap |
| | 9190 | 874 | | ? existing |
| | 9190 | 875 | | : PersistentVoxelIndexMap<IncidenceRowDelta>.Empty; |
| | 9190 | 876 | | if (map.TryGetValue(address.Index, out IncidenceRowDelta delta)) |
| | 4608 | 877 | | return delta; |
| | 4582 | 878 | | delta = new IncidenceRowDelta(); |
| | 4582 | 879 | | long priorMapBytes = hadMap ? map.RetainedBytes : 0; |
| | 4582 | 880 | | int priorMapPages = hadMap ? map.PersistentNodeCount : 0; |
| | 4582 | 881 | | map = map.Set(address.Index, delta); |
| | 4582 | 882 | | _rowDeltas = _rowDeltas.Set(address.MapId, map); |
| | 4582 | 883 | | _rowDeltaInnerBytes = checked( |
| | 4582 | 884 | | _rowDeltaInnerBytes - priorMapBytes + map.RetainedBytes); |
| | 4582 | 885 | | _rowDeltaInnerPages = checked( |
| | 4582 | 886 | | _rowDeltaInnerPages - priorMapPages + map.PersistentNodeCount); |
| | 4582 | 887 | | _rowDeltaValueBytes = checked(_rowDeltaValueBytes + delta.RetainedBytes); |
| | 4582 | 888 | | _rowDeltaValuePages = checked( |
| | 4582 | 889 | | _rowDeltaValuePages + delta.PersistentPageCount); |
| | 4582 | 890 | | return delta; |
| | | 891 | | } |
| | | 892 | | |
| | | 893 | | private void ReleaseCurrentRow() |
| | | 894 | | { |
| | 4578 | 895 | | long priorBytes = _rowDelta!.RetainedBytes; |
| | 4578 | 896 | | int priorPages = _rowDelta.PersistentPageCount; |
| | 4578 | 897 | | _rowDelta.ReleaseAdditions(); |
| | 4578 | 898 | | _rowDeltaValueBytes = checked( |
| | 4578 | 899 | | _rowDeltaValueBytes - priorBytes + _rowDelta.RetainedBytes); |
| | 4578 | 900 | | _rowDeltaValuePages = checked( |
| | 4578 | 901 | | _rowDeltaValuePages - priorPages + _rowDelta.PersistentPageCount); |
| | 4578 | 902 | | _rowDelta = null; |
| | 4578 | 903 | | _priorRowOwners = default; |
| | 4578 | 904 | | _additionRowOwners = default; |
| | 4578 | 905 | | _priorRowRemaining = 0; |
| | 4578 | 906 | | _additionRowRemaining = 0; |
| | 4578 | 907 | | _priorRowOwnerReady = false; |
| | 4578 | 908 | | _additionRowOwnerReady = false; |
| | 4578 | 909 | | _endpointRowBuilder = null; |
| | 4578 | 910 | | _pendingEndpointOwner = default; |
| | 4578 | 911 | | _endpointOwnerPending = false; |
| | 4578 | 912 | | _incidentRowCommitted = false; |
| | 4578 | 913 | | _rowInitialized = false; |
| | 4578 | 914 | | } |
| | | 915 | | |
| | | 916 | | private static bool IsEndpoint( |
| | | 917 | | NavigationCellAddress address, |
| | | 918 | | NavigationConnectionOwnerKey owner, |
| | | 919 | | NavigationConnection definition) => |
| | 18064 | 920 | | address.Equals(new NavigationCellAddress(owner.MapId, definition.SourceIndex)) |
| | 18064 | 921 | | || address.Equals(definition.Destination); |
| | | 922 | | |
| | | 923 | | private static NavigationCellAddress GetRawAddress( |
| | | 924 | | NavigationConnectionOwnerKey owner, |
| | | 925 | | NavigationConnection connection, |
| | | 926 | | int rawIndex) |
| | | 927 | | { |
| | 9191 | 928 | | if (rawIndex == 0) |
| | 4500 | 929 | | return new NavigationCellAddress(owner.MapId, connection.SourceIndex); |
| | 4691 | 930 | | if (rawIndex == connection.Witnesses.Count + 1) |
| | 4500 | 931 | | return connection.Destination; |
| | 191 | 932 | | return connection.Witnesses[rawIndex - 1]; |
| | | 933 | | } |
| | | 934 | | |
| | | 935 | | private void ResetCurrent() |
| | | 936 | | { |
| | 4480 | 937 | | _currentOwner = default; |
| | 4480 | 938 | | _currentSource = null; |
| | 4480 | 939 | | _currentDefinition = null; |
| | 4480 | 940 | | _priorRecord = null; |
| | 4480 | 941 | | _preparedRecord = null; |
| | 4480 | 942 | | _destinationCell = default; |
| | 4480 | 943 | | _semanticIndex = 0; |
| | 4480 | 944 | | _rawAddressIndex = 0; |
| | 4480 | 945 | | _distinctAddressCount = 0; |
| | 4480 | 946 | | _touchAddressIndex = 0; |
| | 4480 | 947 | | _currentInitialized = false; |
| | 4480 | 948 | | _selectionCharged = false; |
| | 4480 | 949 | | _isDormant = false; |
| | 4480 | 950 | | _incidenceUnchanged = false; |
| | 4480 | 951 | | _ownerUpdated = false; |
| | 4480 | 952 | | _componentOwnerJournaled = false; |
| | 4480 | 953 | | _corridorStarted = false; |
| | 4480 | 954 | | _corridorCursor = default; |
| | 4480 | 955 | | _portalBuilder = null; |
| | 4480 | 956 | | _portalCopyIndex = 0; |
| | 4480 | 957 | | } |
| | | 958 | | |
| | | 959 | | private static bool HasComponentEdgeChanged( |
| | | 960 | | NavigationExplicitConnectionRecord? prior, |
| | | 961 | | NavigationExplicitConnectionRecord? next) |
| | | 962 | | { |
| | 4688 | 963 | | bool priorActive = prior?.IsActive ?? false; |
| | 4688 | 964 | | bool nextActive = next?.IsActive ?? false; |
| | 4688 | 965 | | if (priorActive != nextActive) |
| | 4368 | 966 | | return true; |
| | 320 | 967 | | if (!priorActive) |
| | 47 | 968 | | return false; |
| | 273 | 969 | | return prior!.Source != next!.Source |
| | 273 | 970 | | || prior.Destination != next.Destination |
| | 273 | 971 | | || prior.IsLowerBoundCertified != next.IsLowerBoundCertified |
| | 273 | 972 | | || prior.CorridorCost != next.CorridorCost |
| | 273 | 973 | | || !prior.Definition.Equals(next.Definition) |
| | 273 | 974 | | || !PortalsEqual(prior.NavigationPortals, next.NavigationPortals); |
| | | 975 | | } |
| | | 976 | | |
| | | 977 | | private static bool PortalsEqual( |
| | | 978 | | NavigationPagedSequence<GridNavigationPortal> left, |
| | | 979 | | NavigationPagedSequence<GridNavigationPortal> right) |
| | | 980 | | { |
| | | 981 | | System.Diagnostics.Debug.Assert(left.Count == right.Count); |
| | 248 | 982 | | NavigationPagedSequence<GridNavigationPortal>.Enumerator leftValues = |
| | 248 | 983 | | left.GetEnumerator(); |
| | 248 | 984 | | NavigationPagedSequence<GridNavigationPortal>.Enumerator rightValues = |
| | 248 | 985 | | right.GetEnumerator(); |
| | 496 | 986 | | while (leftValues.MoveNext()) |
| | | 987 | | { |
| | 249 | 988 | | rightValues.MoveNext(); |
| | 249 | 989 | | if (!PortalEquals(leftValues.Current, rightValues.Current)) |
| | 1 | 990 | | return false; |
| | | 991 | | } |
| | 247 | 992 | | return true; |
| | | 993 | | } |
| | | 994 | | |
| | | 995 | | private static bool PortalEquals( |
| | | 996 | | GridNavigationPortal left, |
| | | 997 | | GridNavigationPortal right) => |
| | 249 | 998 | | left.FaceKind == right.FaceKind |
| | 249 | 999 | | & left.SourceToTarget == right.SourceToTarget |
| | 249 | 1000 | | & left.CanonicalFacePoint == right.CanonicalFacePoint |
| | 249 | 1001 | | & left.MaximumHorizontalRadius == right.MaximumHorizontalRadius |
| | 249 | 1002 | | & left.MaximumBodyHeight == right.MaximumBodyHeight |
| | 249 | 1003 | | & left.VerticalFaceSegmentStart == right.VerticalFaceSegmentStart |
| | 249 | 1004 | | & left.VerticalFaceSegmentEnd == right.VerticalFaceSegmentEnd; |
| | | 1005 | | |
| | | 1006 | | private bool AdvanceGather(MaintenanceWorkMeter meter) |
| | | 1007 | | { |
| | 1849 | 1008 | | if (_transaction != null) |
| | 296 | 1009 | | return AdvanceOverlayGather(meter); |
| | 7125 | 1010 | | while (_stage < 4) |
| | | 1011 | | { |
| | 5732 | 1012 | | _candidate._maps.TryGetValue(_mapId!, out MapState state); |
| | 5732 | 1013 | | int count = _stage switch |
| | 5732 | 1014 | | { |
| | 1431 | 1015 | | 0 => _candidate._explicitConnections.GetSourceOwnerCount(_mapId!), |
| | 1473 | 1016 | | 1 => _candidate._explicitConnections.GetIncidentAddressCount(_mapId!), |
| | 1435 | 1017 | | 2 => state == null ? 0 : state.Map.ConnectionSpan.Length, |
| | 1393 | 1018 | | _ => state == null ? 0 : state.Overlay.ConnectionCount |
| | 5732 | 1019 | | }; |
| | 6124 | 1020 | | while (_itemIndex < count) |
| | | 1021 | | { |
| | 552 | 1022 | | if (_stage == 1) |
| | | 1023 | | { |
| | 134 | 1024 | | NavigationCellAddress address = _candidate._explicitConnections |
| | 134 | 1025 | | .GetIncidentAddressAt(_mapId!, _itemIndex); |
| | 134 | 1026 | | if (!AdvanceIncidentOwners(address, meter)) |
| | 80 | 1027 | | return false; |
| | 54 | 1028 | | _itemIndex++; |
| | 54 | 1029 | | continue; |
| | | 1030 | | } |
| | 418 | 1031 | | NavigationConnectionOwnerKey owner = _stage switch |
| | 418 | 1032 | | { |
| | 93 | 1033 | | 0 => _candidate._explicitConnections |
| | 93 | 1034 | | .GetSourceOwnerAt(_mapId!, _itemIndex).Owner, |
| | 322 | 1035 | | 2 => new NavigationConnectionOwnerKey( |
| | 322 | 1036 | | _mapId!, |
| | 322 | 1037 | | state!.Map.ConnectionSpan[_itemIndex].Id), |
| | 3 | 1038 | | _ => new NavigationConnectionOwnerKey( |
| | 3 | 1039 | | _mapId!, |
| | 3 | 1040 | | state!.Overlay.GetConnectionAt(_itemIndex).Id) |
| | 418 | 1041 | | }; |
| | 418 | 1042 | | if (!AddOwner(owner, meter)) |
| | 80 | 1043 | | return false; |
| | 338 | 1044 | | _itemIndex++; |
| | | 1045 | | } |
| | 5572 | 1046 | | _stage++; |
| | 5572 | 1047 | | _itemIndex = 0; |
| | | 1048 | | } |
| | 1393 | 1049 | | return true; |
| | | 1050 | | } |
| | | 1051 | | |
| | | 1052 | | private bool AdvanceOverlayGather(MaintenanceWorkMeter meter) |
| | | 1053 | | { |
| | 296 | 1054 | | ReadOnlySpan<NavigationMapOverlayDelta> maps = _transaction!.MapSpan; |
| | 635 | 1055 | | while (_mapIndex < maps.Length) |
| | | 1056 | | { |
| | 343 | 1057 | | NavigationMapOverlayDelta map = maps[_mapIndex]; |
| | 343 | 1058 | | if (_stage == 0) |
| | | 1059 | | { |
| | 342 | 1060 | | ReadOnlySpan<NavigationCellOverlayOperation> cells = map.CellSpan; |
| | 17526 | 1061 | | while (_itemIndex < cells.Length) |
| | | 1062 | | { |
| | 17187 | 1063 | | if (!AdvanceIncidentOwners( |
| | 17187 | 1064 | | new NavigationCellAddress(map.MapId, cells[_itemIndex].Index), |
| | 17187 | 1065 | | meter)) |
| | | 1066 | | { |
| | 3 | 1067 | | return false; |
| | | 1068 | | } |
| | 17184 | 1069 | | _itemIndex++; |
| | | 1070 | | } |
| | 339 | 1071 | | _stage = 1; |
| | 339 | 1072 | | _itemIndex = 0; |
| | | 1073 | | } |
| | 340 | 1074 | | ReadOnlySpan<NavigationConnectionOverlayOperation> connections = map.ConnectionSpan; |
| | 4496 | 1075 | | while (_itemIndex < connections.Length) |
| | | 1076 | | { |
| | 4157 | 1077 | | if (!AddOwner( |
| | 4157 | 1078 | | new NavigationConnectionOwnerKey(map.MapId, connections[_itemIndex].Id), |
| | 4157 | 1079 | | meter)) |
| | 1 | 1080 | | return false; |
| | 4156 | 1081 | | _itemIndex++; |
| | | 1082 | | } |
| | 339 | 1083 | | _mapIndex++; |
| | 339 | 1084 | | _stage = 0; |
| | 339 | 1085 | | _itemIndex = 0; |
| | | 1086 | | } |
| | 292 | 1087 | | _stage = 4; |
| | 292 | 1088 | | return true; |
| | | 1089 | | } |
| | | 1090 | | |
| | | 1091 | | private bool AddOwner(NavigationConnectionOwnerKey owner, MaintenanceWorkMeter meter) |
| | | 1092 | | { |
| | 4835 | 1093 | | if (!meter.TryConsumeDependencyEntries(1)) |
| | 164 | 1094 | | return false; |
| | 4671 | 1095 | | PersistentStringMap<bool> map = _owners.TryGetValue( |
| | 4671 | 1096 | | owner.MapId, |
| | 4671 | 1097 | | out PersistentStringMap<bool> existing) |
| | 4671 | 1098 | | ? existing |
| | 4671 | 1099 | | : PersistentStringMap<bool>.Empty; |
| | 4671 | 1100 | | if (!map.ContainsKey(owner.ConnectionId)) |
| | | 1101 | | { |
| | 4497 | 1102 | | long priorBytes = map.RetainedBytes; |
| | 4497 | 1103 | | int priorPages = map.PersistentNodeCount; |
| | 4497 | 1104 | | map = map.Set(owner.ConnectionId, true); |
| | 4497 | 1105 | | _owners = _owners.Set(owner.MapId, map); |
| | 4497 | 1106 | | _innerOwnerBytes = checked( |
| | 4497 | 1107 | | _innerOwnerBytes - priorBytes + map.RetainedBytes); |
| | 4497 | 1108 | | _innerOwnerPages = checked( |
| | 4497 | 1109 | | _innerOwnerPages - priorPages + map.PersistentNodeCount); |
| | | 1110 | | } |
| | 4671 | 1111 | | if (!_candidate._explicitChangedSources.ContainsKey(owner.MapId)) |
| | | 1112 | | { |
| | 202 | 1113 | | _candidate._explicitChangedSources = |
| | 202 | 1114 | | _candidate._explicitChangedSources.Set(owner.MapId, true); |
| | | 1115 | | } |
| | 4671 | 1116 | | return true; |
| | | 1117 | | } |
| | | 1118 | | |
| | | 1119 | | private bool AdvanceIncidentOwners( |
| | | 1120 | | NavigationCellAddress address, |
| | | 1121 | | MaintenanceWorkMeter meter) |
| | | 1122 | | { |
| | 17321 | 1123 | | if (!_incidentOwnerEnumerationStarted) |
| | | 1124 | | { |
| | 17238 | 1125 | | _incidentOwnerEnumerator = |
| | 17238 | 1126 | | _candidate._explicitConnections.GetIncidentOwnerEnumerator(address); |
| | 17238 | 1127 | | _incidentOwnerEnumerationStarted = true; |
| | | 1128 | | } |
| | 177 | 1129 | | while (true) |
| | | 1130 | | { |
| | 17498 | 1131 | | if (!_hasPendingIncidentOwner) |
| | | 1132 | | { |
| | 17415 | 1133 | | if (!_incidentOwnerEnumerator.MoveNext()) |
| | | 1134 | | { |
| | 17238 | 1135 | | _incidentOwnerEnumerationStarted = false; |
| | 17238 | 1136 | | return true; |
| | | 1137 | | } |
| | 177 | 1138 | | _pendingIncidentOwner = _incidentOwnerEnumerator.Current; |
| | 177 | 1139 | | _hasPendingIncidentOwner = true; |
| | | 1140 | | } |
| | 260 | 1141 | | if (!AddOwner(_pendingIncidentOwner, meter)) |
| | 83 | 1142 | | return false; |
| | 177 | 1143 | | _hasPendingIncidentOwner = false; |
| | | 1144 | | } |
| | | 1145 | | } |
| | | 1146 | | |
| | | 1147 | | private sealed class IncidenceRowDelta |
| | | 1148 | | { |
| | | 1149 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder? |
| | | 1150 | | _additionBuilder; |
| | | 1151 | | private NavigationPagedSequence<NavigationConnectionOwnerKey>? _additions; |
| | | 1152 | | |
| | 40882 | 1153 | | internal long RetainedBytes => checked( |
| | 40882 | 1154 | | 40L |
| | 40882 | 1155 | | + (_additionBuilder?.RetainedBytes ?? _additions?.RetainedBytes ?? 0)); |
| | | 1156 | | |
| | 40882 | 1157 | | internal int PersistentPageCount => checked( |
| | 40882 | 1158 | | 1 |
| | 40882 | 1159 | | + (_additionBuilder?.PersistentPageCount |
| | 40882 | 1160 | | ?? _additions?.PersistentPageCount |
| | 40882 | 1161 | | ?? 0)); |
| | | 1162 | | |
| | | 1163 | | internal void AppendAddition(NavigationConnectionOwnerKey owner) |
| | | 1164 | | { |
| | 8994 | 1165 | | _additionBuilder ??= |
| | 8994 | 1166 | | new NavigationPagedSequence<NavigationConnectionOwnerKey>.Builder(16); |
| | 8994 | 1167 | | _additionBuilder.Append(owner); |
| | 8994 | 1168 | | } |
| | | 1169 | | |
| | | 1170 | | internal bool EndpointTouched { get; private set; } |
| | | 1171 | | |
| | 9000 | 1172 | | internal void MarkEndpointTouched() => EndpointTouched = true; |
| | | 1173 | | |
| | | 1174 | | internal NavigationPagedSequence<NavigationConnectionOwnerKey> SealAdditions() |
| | | 1175 | | { |
| | 4578 | 1176 | | _additions = _additionBuilder?.Seal() |
| | 4578 | 1177 | | ?? NavigationPagedSequence<NavigationConnectionOwnerKey>.Empty; |
| | 4578 | 1178 | | _additionBuilder = null; |
| | 4578 | 1179 | | return _additions; |
| | | 1180 | | } |
| | | 1181 | | |
| | | 1182 | | internal void ReleaseAdditions() |
| | | 1183 | | { |
| | 4578 | 1184 | | _additionBuilder = null; |
| | 4578 | 1185 | | _additions = null; |
| | 4578 | 1186 | | } |
| | | 1187 | | } |
| | | 1188 | | |
| | | 1189 | | private struct IncidenceOwnerTree |
| | | 1190 | | { |
| | | 1191 | | private Node? _root; |
| | | 1192 | | private int _count; |
| | | 1193 | | |
| | 2813 | 1194 | | internal long RetainedBytes => checked((long)_count * 64L); |
| | | 1195 | | |
| | 2556 | 1196 | | internal int PersistentPageCount => _count; |
| | | 1197 | | |
| | | 1198 | | internal void Add( |
| | | 1199 | | NavigationConnectionOwnerKey owner, |
| | | 1200 | | NavigationExplicitConnectionIndex index) |
| | | 1201 | | { |
| | 4414 | 1202 | | if (_root == null) |
| | | 1203 | | { |
| | 161 | 1204 | | _root = new Node(owner, null); |
| | 161 | 1205 | | _count = 1; |
| | 161 | 1206 | | return; |
| | | 1207 | | } |
| | 4253 | 1208 | | Node current = _root; |
| | | 1209 | | while (true) |
| | | 1210 | | { |
| | 37510 | 1211 | | int comparison = index.CompareOwners(owner, current.Owner); |
| | | 1212 | | System.Diagnostics.Debug.Assert(comparison != 0); |
| | 37510 | 1213 | | if (comparison < 0) |
| | | 1214 | | { |
| | 16 | 1215 | | if (current.Left != null) |
| | | 1216 | | { |
| | 5 | 1217 | | current = current.Left; |
| | 5 | 1218 | | continue; |
| | | 1219 | | } |
| | 11 | 1220 | | current.Left = new Node(owner, current); |
| | 11 | 1221 | | _count++; |
| | 11 | 1222 | | Rebalance(current); |
| | 11 | 1223 | | return; |
| | | 1224 | | } |
| | 37494 | 1225 | | if (current.Right != null) |
| | | 1226 | | { |
| | 33252 | 1227 | | current = current.Right; |
| | 33252 | 1228 | | continue; |
| | | 1229 | | } |
| | 4242 | 1230 | | current.Right = new Node(owner, current); |
| | 4242 | 1231 | | _count++; |
| | 4242 | 1232 | | Rebalance(current); |
| | 4242 | 1233 | | return; |
| | | 1234 | | } |
| | | 1235 | | } |
| | | 1236 | | |
| | | 1237 | | internal Node? GetSuccessor(Node? current) |
| | | 1238 | | { |
| | 6475 | 1239 | | if (current == null) |
| | 2062 | 1240 | | return Minimum(_root); |
| | 4413 | 1241 | | if (current.Right != null) |
| | 2130 | 1242 | | return Minimum(current.Right); |
| | 2283 | 1243 | | Node child = current; |
| | 2283 | 1244 | | Node? parent = current.Parent; |
| | 4413 | 1245 | | while (parent != null && ReferenceEquals(parent.Right, child)) |
| | | 1246 | | { |
| | 2130 | 1247 | | child = parent; |
| | 2130 | 1248 | | parent = parent.Parent; |
| | | 1249 | | } |
| | 2283 | 1250 | | return parent; |
| | | 1251 | | } |
| | | 1252 | | |
| | | 1253 | | internal void Clear() |
| | | 1254 | | { |
| | 2061 | 1255 | | _root = null; |
| | 2061 | 1256 | | _count = 0; |
| | 2061 | 1257 | | } |
| | | 1258 | | |
| | | 1259 | | private static Node? Minimum(Node? node) |
| | | 1260 | | { |
| | 6315 | 1261 | | while (node?.Left != null) |
| | 2123 | 1262 | | node = node.Left; |
| | 4192 | 1263 | | return node; |
| | | 1264 | | } |
| | | 1265 | | |
| | | 1266 | | private void Rebalance(Node? node) |
| | | 1267 | | { |
| | 41763 | 1268 | | while (node != null) |
| | | 1269 | | { |
| | 37510 | 1270 | | UpdateHeight(node); |
| | 37510 | 1271 | | int balance = Height(node.Left) - Height(node.Right); |
| | 37510 | 1272 | | if (balance > 1) |
| | | 1273 | | { |
| | 2 | 1274 | | if (Height(node.Left!.Left) < Height(node.Left.Right)) |
| | 1 | 1275 | | RotateLeft(node.Left); |
| | 2 | 1276 | | node = RotateRight(node); |
| | | 1277 | | } |
| | 37508 | 1278 | | else if (balance < -1) |
| | | 1279 | | { |
| | 4171 | 1280 | | if (Height(node.Right!.Right) < Height(node.Right.Left)) |
| | 1 | 1281 | | RotateRight(node.Right); |
| | 4171 | 1282 | | node = RotateLeft(node); |
| | | 1283 | | } |
| | 37510 | 1284 | | node = node.Parent; |
| | | 1285 | | } |
| | 4253 | 1286 | | } |
| | | 1287 | | |
| | | 1288 | | private Node RotateLeft(Node node) |
| | | 1289 | | { |
| | 4172 | 1290 | | Node pivot = node.Right!; |
| | 4172 | 1291 | | ReplaceParentLink(node, pivot); |
| | 4172 | 1292 | | node.Right = pivot.Left; |
| | 4172 | 1293 | | if (node.Right != null) |
| | 2058 | 1294 | | node.Right.Parent = node; |
| | 4172 | 1295 | | pivot.Left = node; |
| | 4172 | 1296 | | node.Parent = pivot; |
| | 4172 | 1297 | | UpdateHeight(node); |
| | 4172 | 1298 | | UpdateHeight(pivot); |
| | 4172 | 1299 | | return pivot; |
| | | 1300 | | } |
| | | 1301 | | |
| | | 1302 | | private Node RotateRight(Node node) |
| | | 1303 | | { |
| | 3 | 1304 | | Node pivot = node.Left!; |
| | 3 | 1305 | | ReplaceParentLink(node, pivot); |
| | 3 | 1306 | | node.Left = pivot.Right; |
| | 3 | 1307 | | if (node.Left != null) |
| | 1 | 1308 | | node.Left.Parent = node; |
| | 3 | 1309 | | pivot.Right = node; |
| | 3 | 1310 | | node.Parent = pivot; |
| | 3 | 1311 | | UpdateHeight(node); |
| | 3 | 1312 | | UpdateHeight(pivot); |
| | 3 | 1313 | | return pivot; |
| | | 1314 | | } |
| | | 1315 | | |
| | | 1316 | | private void ReplaceParentLink(Node node, Node replacement) |
| | | 1317 | | { |
| | 4175 | 1318 | | Node? parent = node.Parent; |
| | 4175 | 1319 | | replacement.Parent = parent; |
| | 4175 | 1320 | | if (parent == null) |
| | 57 | 1321 | | _root = replacement; |
| | 4118 | 1322 | | else if (ReferenceEquals(parent.Left, node)) |
| | 1 | 1323 | | parent.Left = replacement; |
| | | 1324 | | else |
| | 4117 | 1325 | | parent.Right = replacement; |
| | 4117 | 1326 | | } |
| | | 1327 | | |
| | 175086 | 1328 | | private static int Height(Node? node) => node?.Height ?? 0; |
| | | 1329 | | |
| | | 1330 | | private static void UpdateHeight(Node node) => |
| | 45860 | 1331 | | node.Height = 1 + Math.Max(Height(node.Left), Height(node.Right)); |
| | | 1332 | | |
| | | 1333 | | internal sealed class Node |
| | | 1334 | | { |
| | 4414 | 1335 | | internal Node(NavigationConnectionOwnerKey owner, Node? parent) |
| | | 1336 | | { |
| | 4414 | 1337 | | Owner = owner; |
| | 4414 | 1338 | | Parent = parent; |
| | 4414 | 1339 | | } |
| | | 1340 | | |
| | | 1341 | | internal NavigationConnectionOwnerKey Owner { get; } |
| | | 1342 | | |
| | | 1343 | | internal Node? Parent { get; set; } |
| | | 1344 | | |
| | | 1345 | | internal Node? Left { get; set; } |
| | | 1346 | | |
| | | 1347 | | internal Node? Right { get; set; } |
| | | 1348 | | |
| | | 1349 | | internal int Height { get; set; } = 1; |
| | | 1350 | | } |
| | | 1351 | | } |
| | | 1352 | | } |
| | | 1353 | | } |