| | | 1 | | //======================================================================= |
| | | 2 | | // GraphDependencyStamp.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 SwiftCollections.Utility; |
| | | 10 | | |
| | | 11 | | namespace Trailblazer.Pathing; |
| | | 12 | | |
| | | 13 | | /// <summary>Captures exact immutable component and page dependencies without a global graph version.</summary> |
| | | 14 | | internal sealed class GraphDependencyStamp |
| | | 15 | | { |
| | | 16 | | private const long ObjectHeaderBytes = 16L; |
| | | 17 | | private const long ArrayHeaderBytes = 24L; |
| | | 18 | | private const long ReferenceSlotBytes = 8L; |
| | | 19 | | private const long Int64Bytes = 8L; |
| | | 20 | | private const long NavigationAreaPolicyKeyBytes = ReferenceSlotBytes + Int64Bytes; |
| | | 21 | | private const long GraphComponentDependencyBytes = 32L; |
| | | 22 | | private const long GraphPageDependencyBytes = 56L; |
| | | 23 | | private static readonly long BaseRetainedBytes = Align8( |
| | | 24 | | ObjectHeaderBytes |
| | | 25 | | + NavigationAreaPolicyKeyBytes |
| | | 26 | | + (2L * ReferenceSlotBytes) |
| | | 27 | | + Int64Bytes |
| | | 28 | | + Int64Bytes); |
| | | 29 | | |
| | | 30 | | internal GraphDependencyStamp( |
| | | 31 | | NavigationAreaPolicyKey areaPolicy, |
| | | 32 | | GraphComponentDependency[] components, |
| | | 33 | | GraphPageDependency[] pages, |
| | | 34 | | bool hasTransitionRuleDependency = false, |
| | | 35 | | long transitionRuleVersion = 0) |
| | | 36 | | { |
| | | 37 | | AreaPolicy = areaPolicy; |
| | | 38 | | Components = components; |
| | | 39 | | Pages = pages; |
| | | 40 | | HasTransitionRuleDependency = hasTransitionRuleDependency; |
| | | 41 | | TransitionRuleVersion = hasTransitionRuleDependency ? transitionRuleVersion : 0; |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | internal NavigationAreaPolicyKey AreaPolicy { get; } |
| | | 45 | | |
| | | 46 | | internal GraphComponentDependency[] Components { get; } |
| | | 47 | | |
| | | 48 | | internal GraphPageDependency[] Pages { get; } |
| | | 49 | | |
| | | 50 | | internal bool HasTransitionRuleDependency { get; } |
| | | 51 | | |
| | | 52 | | internal long TransitionRuleVersion { get; } |
| | | 53 | | |
| | | 54 | | internal long RetainedBytes => checked( |
| | | 55 | | BaseRetainedBytes |
| | | 56 | | + GetArrayRetainedBytes(Components, GraphComponentDependencyBytes) |
| | | 57 | | + GetArrayRetainedBytes(Pages, GraphPageDependencyBytes)); |
| | | 58 | | |
| | | 59 | | internal static long GetRetainedBytes(int componentCount, int pageCount) |
| | | 60 | | { |
| | | 61 | | SwiftThrowHelper.ThrowIfNegative(componentCount, nameof(componentCount)); |
| | | 62 | | SwiftThrowHelper.ThrowIfNegative(pageCount, nameof(pageCount)); |
| | | 63 | | return checked( |
| | | 64 | | BaseRetainedBytes |
| | | 65 | | + GetArrayRetainedBytes(componentCount, GraphComponentDependencyBytes) |
| | | 66 | | + GetArrayRetainedBytes(pageCount, GraphPageDependencyBytes)); |
| | | 67 | | } |
| | | 68 | | |
| | | 69 | | private static long GetArrayRetainedBytes<T>(T[] values, long elementBytes) => |
| | | 70 | | values.Length == 0 |
| | | 71 | | ? 0L |
| | | 72 | | : Align8(checked(ArrayHeaderBytes + ((long)values.Length * elementBytes))); |
| | | 73 | | |
| | | 74 | | private static long GetArrayRetainedBytes(int length, long elementBytes) => |
| | | 75 | | length == 0 |
| | | 76 | | ? 0L |
| | | 77 | | : Align8(checked(ArrayHeaderBytes + ((long)length * elementBytes))); |
| | | 78 | | |
| | | 79 | | private static long Align8(long value) => checked((value + 7L) & ~7L); |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | /// <summary>Identifies one structural component generation.</summary> |
| | | 83 | | internal readonly struct GraphComponentDependency : IEquatable<GraphComponentDependency> |
| | | 84 | | { |
| | | 85 | | internal GraphComponentDependency(NavigationSurfaceComponentKey key, long version) |
| | | 86 | | { |
| | | 87 | | Key = key; |
| | | 88 | | Version = version; |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | internal NavigationSurfaceComponentKey Key { get; } |
| | | 92 | | |
| | | 93 | | internal long Version { get; } |
| | | 94 | | |
| | | 95 | | public bool Equals(GraphComponentDependency other) => |
| | | 96 | | Key.Equals(other.Key) |
| | | 97 | | && Version == other.Version; |
| | | 98 | | |
| | | 99 | | public override int GetHashCode() |
| | | 100 | | { |
| | | 101 | | return SwiftHashTools.CombineHashCodes(Key.GetHashCode(), Version.GetHashCode()); |
| | | 102 | | } |
| | | 103 | | } |
| | | 104 | | |
| | | 105 | | /// <summary>Names one exact map-local page whose versions a query consumed.</summary> |
| | | 106 | | internal readonly struct GraphPageDependencyAddress |
| | | 107 | | { |
| | | 108 | | internal GraphPageDependencyAddress(string mapId, int pageIndex) |
| | | 109 | | { |
| | | 110 | | MapId = mapId; |
| | | 111 | | PageIndex = pageIndex; |
| | | 112 | | } |
| | | 113 | | |
| | | 114 | | internal string MapId { get; } |
| | | 115 | | |
| | | 116 | | internal int PageIndex { get; } |
| | | 117 | | } |
| | | 118 | | |
| | | 119 | | /// <summary>Identifies one map-local semantic and physical page generation.</summary> |
| | | 120 | | internal readonly struct GraphPageDependency : IEquatable<GraphPageDependency> |
| | | 121 | | { |
| | | 122 | | internal GraphPageDependency( |
| | | 123 | | string mapId, |
| | | 124 | | long bakeVersion, |
| | | 125 | | long dynamicSlotGeneration, |
| | | 126 | | int pageIndex, |
| | | 127 | | long semanticVersion, |
| | | 128 | | long physicalVersion, |
| | | 129 | | long transitionVersion = 0) |
| | | 130 | | { |
| | 32918 | 131 | | MapId = mapId; |
| | 32918 | 132 | | BakeVersion = bakeVersion; |
| | 32918 | 133 | | DynamicSlotGeneration = dynamicSlotGeneration; |
| | 32918 | 134 | | PageIndex = pageIndex; |
| | 32918 | 135 | | SemanticVersion = semanticVersion; |
| | 32918 | 136 | | PhysicalVersion = physicalVersion; |
| | 32918 | 137 | | TransitionVersion = transitionVersion; |
| | 32918 | 138 | | } |
| | | 139 | | |
| | | 140 | | internal string MapId { get; } |
| | | 141 | | internal long BakeVersion { get; } |
| | | 142 | | internal long DynamicSlotGeneration { get; } |
| | | 143 | | internal int PageIndex { get; } |
| | | 144 | | internal long SemanticVersion { get; } |
| | | 145 | | internal long PhysicalVersion { get; } |
| | | 146 | | internal long TransitionVersion { get; } |
| | | 147 | | |
| | | 148 | | public bool Equals(GraphPageDependency other) => |
| | 28728 | 149 | | string.Equals(MapId, other.MapId, StringComparison.Ordinal) |
| | 28728 | 150 | | && BakeVersion == other.BakeVersion |
| | 28728 | 151 | | && DynamicSlotGeneration == other.DynamicSlotGeneration |
| | 28728 | 152 | | && PageIndex == other.PageIndex |
| | 28728 | 153 | | && SemanticVersion == other.SemanticVersion |
| | 28728 | 154 | | && PhysicalVersion == other.PhysicalVersion |
| | 28728 | 155 | | && TransitionVersion == other.TransitionVersion; |
| | | 156 | | |
| | | 157 | | public override int GetHashCode() |
| | | 158 | | { |
| | 7 | 159 | | int mapHash = MapId == null |
| | 7 | 160 | | ? 0 |
| | 7 | 161 | | : SwiftHashTools.GetDeterministicStringEqualityComparer().GetHashCode(MapId); |
| | 7 | 162 | | int hash = SwiftHashTools.CombineHashCodes(mapHash, BakeVersion.GetHashCode()); |
| | 7 | 163 | | hash = SwiftHashTools.CombineHashCodes(hash, DynamicSlotGeneration.GetHashCode()); |
| | 7 | 164 | | hash = SwiftHashTools.CombineHashCodes(hash, PageIndex); |
| | 7 | 165 | | hash = SwiftHashTools.CombineHashCodes(hash, SemanticVersion.GetHashCode()); |
| | 7 | 166 | | hash = SwiftHashTools.CombineHashCodes(hash, PhysicalVersion.GetHashCode()); |
| | 7 | 167 | | return SwiftHashTools.CombineHashCodes(hash, TransitionVersion.GetHashCode()); |
| | | 168 | | } |
| | | 169 | | } |