| | | 1 | | //======================================================================= |
| | | 2 | | // PreparedNavigationMap.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 | | |
| | | 10 | | namespace Trailblazer.Pathing; |
| | | 11 | | |
| | | 12 | | /// <summary>Wraps an inert validated map bake before deterministic runtime admission.</summary> |
| | | 13 | | public sealed class PreparedNavigationMap |
| | | 14 | | { |
| | | 15 | | private const long DefaultCellRetainedBytes = 64L; |
| | | 16 | | private const long TransitionRuleRetainedBytes = 80L; |
| | | 17 | | |
| | | 18 | | /// <summary>Creates an inert prepared-map descriptor from a validated immutable map.</summary> |
| | 2014 | 19 | | public PreparedNavigationMap( |
| | 2014 | 20 | | NavigationMap map, |
| | 2014 | 21 | | long bakeVersion, |
| | 2014 | 22 | | NavigationMapCheckpointStamp? checkpointStamp = null) |
| | | 23 | | { |
| | 2014 | 24 | | SwiftThrowHelper.ThrowIfNull(map, nameof(map)); |
| | 2014 | 25 | | SwiftThrowHelper.ThrowIfArgument(bakeVersion <= 0, nameof(bakeVersion)); |
| | 2014 | 26 | | SwiftThrowHelper.ThrowIfArgument( |
| | 2014 | 27 | | checkpointStamp.HasValue |
| | 2014 | 28 | | && !string.Equals(checkpointStamp.Value.MapId, map.MapId, System.StringComparison.Ordinal), |
| | 2014 | 29 | | nameof(checkpointStamp), |
| | 2014 | 30 | | "Checkpoint map id must match the prepared map."); |
| | | 31 | | |
| | 2014 | 32 | | Map = map; |
| | 2014 | 33 | | BakeVersion = bakeVersion; |
| | 2014 | 34 | | BakedCellLookup = NavigationBakedCellLookup.Create(map); |
| | 2014 | 35 | | RetainedBytes = NavigationByteCount.SaturatingAdd( |
| | 2014 | 36 | | EstimateRetainedBytes(map), |
| | 2014 | 37 | | BakedCellLookup.RetainedBytes); |
| | 2014 | 38 | | CheckpointStamp = checkpointStamp; |
| | 2014 | 39 | | } |
| | | 40 | | |
| | | 41 | | /// <summary>Gets the immutable prepared map asset.</summary> |
| | | 42 | | public NavigationMap Map { get; } |
| | | 43 | | |
| | | 44 | | /// <summary>Gets the immutable bake identity assigned during preparation.</summary> |
| | | 45 | | public long BakeVersion { get; } |
| | | 46 | | |
| | | 47 | | /// <summary>Gets the deterministic retained-byte reservation for the prepared bake.</summary> |
| | | 48 | | public long RetainedBytes { get; } |
| | | 49 | | |
| | | 50 | | /// <summary>Gets the checkpoint base stamp, when this preparation absorbed overlay state.</summary> |
| | | 51 | | public NavigationMapCheckpointStamp? CheckpointStamp { get; } |
| | | 52 | | |
| | | 53 | | internal NavigationBakedCellLookup BakedCellLookup { get; } |
| | | 54 | | |
| | | 55 | | private static long EstimateRetainedBytes(NavigationMap map) |
| | | 56 | | { |
| | 2014 | 57 | | long bytes = NavigationByteCount.SaturatingAdd( |
| | 2014 | 58 | | 128L, |
| | 2014 | 59 | | (long)map.MapId.Length * sizeof(char)); |
| | 2014 | 60 | | bytes = NavigationByteCount.SaturatingAdd(bytes, map.NativePortalTemplateRetainedBytes); |
| | 2014 | 61 | | bytes = NavigationByteCount.SaturatingAdd(bytes, (long)map.Cells.Count * 96L); |
| | 2014 | 62 | | if (map.DefaultCell.HasValue) |
| | 61 | 63 | | bytes = NavigationByteCount.SaturatingAdd(bytes, DefaultCellRetainedBytes); |
| | 2014 | 64 | | ReadOnlySpan<TraversalTransitionRule> rules = map.TransitionRuleSpan; |
| | 4170 | 65 | | for (int i = 0; i < rules.Length; i++) |
| | | 66 | | { |
| | 71 | 67 | | bytes = NavigationByteCount.SaturatingAdd(bytes, TransitionRuleRetainedBytes); |
| | 71 | 68 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 71 | 69 | | bytes, |
| | 71 | 70 | | (long)rules[i].Id.Length * sizeof(char)); |
| | | 71 | | } |
| | 4592 | 72 | | for (int i = 0; i < map.Connections.Count; i++) |
| | | 73 | | { |
| | 282 | 74 | | NavigationConnection connection = map.Connections[i]; |
| | 282 | 75 | | bytes = NavigationByteCount.SaturatingAdd(bytes, 160L); |
| | 282 | 76 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 282 | 77 | | bytes, |
| | 282 | 78 | | (long)connection.Id.Length * sizeof(char)); |
| | 282 | 79 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 282 | 80 | | bytes, |
| | 282 | 81 | | (long)connection.Destination.MapId.Length * sizeof(char)); |
| | 980 | 82 | | for (int witness = 0; witness < connection.Witnesses.Count; witness++) |
| | | 83 | | { |
| | 208 | 84 | | bytes = NavigationByteCount.SaturatingAdd(bytes, 32L); |
| | 208 | 85 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 208 | 86 | | bytes, |
| | 208 | 87 | | (long)connection.Witnesses[witness].MapId.Length * sizeof(char)); |
| | | 88 | | } |
| | | 89 | | } |
| | | 90 | | |
| | 4312 | 91 | | for (int i = 0; i < map.Transitions.Count; i++) |
| | | 92 | | { |
| | 142 | 93 | | TraversalTransitionDefinition transition = map.Transitions[i]; |
| | 142 | 94 | | bytes = NavigationByteCount.SaturatingAdd(bytes, 144L); |
| | 142 | 95 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 142 | 96 | | bytes, |
| | 142 | 97 | | (long)transition.Id.Length * sizeof(char)); |
| | 142 | 98 | | bytes = NavigationByteCount.SaturatingAdd( |
| | 142 | 99 | | bytes, |
| | 142 | 100 | | (long)transition.Destination.MapId.Length * sizeof(char)); |
| | | 101 | | } |
| | | 102 | | |
| | 2014 | 103 | | return bytes; |
| | | 104 | | } |
| | | 105 | | } |