/// <summary> /// Makes a tree that represents an observed input on a path into a subtree of /// a tree that represents the corresponding path in the pathset. /// /// <see cref="MergeStrongFingerprintAndPathSetTrees(JsonNode, JsonNode)"/> /// for numbering explanation. /// /// Converts /// [3] "E":"VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" /// => /// [3'] "ObservedInput":"E:VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" /// /// Reparent [3'] from /// [2] "ObservedInputs":"" /// to /// [5'] "B:/out/objects/n/x/qbkexxlc8je93wycw7yrlw0a305n7k/xunit-out/CacheMissAnaAD836B23/3/obj/readonly/src_0":"" /// /// Add /// [8] "Members":"[src_1, src_2]" /// to /// [5'] "B:/out/objects/n/x/qbkexxlc8je93wycw7yrlw0a305n7k/xunit-out/CacheMissAnaAD836B23/3/obj/readonly/src_0":"" /// </summary> /// <param name="observedInputNode"></param> /// <param name="pathSetNode"></param> private void ReparentObservedInput(JsonNode observedInputNode, JsonNode pathSetNode) { // Store values from // [3] "E":"VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" // before manipulating the node var observedInputType = observedInputNode.Name; var observedInputHash = observedInputNode.Values[0]; var values = observedInputNode.Values; values.Clear(); switch (observedInputType) { case ObservedInputConstants.AbsentPathProbe: values.Add(ObservedInputType.AbsentPathProbe.ToString()); break; case ObservedInputConstants.FileContentRead: values.Add($"{ObservedInputType.FileContentRead.ToString()}:{observedInputHash}"); break; case ObservedInputConstants.DirectoryEnumeration: values.Add($"{ObservedInputType.DirectoryEnumeration.ToString()}:{observedInputHash}"); // [8] "Members":"[src_1, src_2]" AddDirectoryMembershipBranch(observedInputHash, pathSetNode); break; case ObservedInputConstants.ExistingDirectoryProbe: values.Add(ObservedInputType.ExistingDirectoryProbe.ToString()); break; case ObservedInputConstants.ExistingFileProbe: values.Add(ObservedInputType.ExistingFileProbe.ToString()); break; } // [3'] "ObservedInput":"E:VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" observedInputNode.Name = ObservedInputConstants.ObservedInputs; JsonTree.ReparentBranch(observedInputNode, pathSetNode); }
/// <summary> /// Adds a directory membership tree as a sub-tree to a given path set tree. /// </summary> /// <param name="directoryFingerprint"> /// The directory fingerprint to look up membership. /// </param> /// <param name="pathSetNode"> /// The path set node that represents the directory and the parent node. /// </param> private void AddDirectoryMembershipBranch(string directoryFingerprint, JsonNode pathSetNode) { if (m_store.TryGetContentHashValue(directoryFingerprint, out string inputs)) { WriteToPipFile(PrettyFormatJsonField(new KeyValuePair <string, string>(directoryFingerprint, inputs)).ToString()); var directoryMembershipTree = JsonTree.Deserialize(inputs); for (var it = directoryMembershipTree.Children.First; it != null; it = it.Next) { JsonTree.ReparentBranch(it.Value, pathSetNode); } } else { // Include a node for the directory membership, but use an error message as the value var placeholder = new JsonNode { Name = directoryFingerprint }; placeholder.Values.Add(CacheMissAnalysisUtilities.RepeatedStrings.MissingDirectoryMembershipFingerprint); JsonTree.ReparentBranch(placeholder, pathSetNode); } }
/// <summary> /// Path set hash inputs are stored separately from the strong fingerprint inputs. /// This merges the path set hash inputs tree into the strong fingerprint inputs tree /// while maintaining the 1:1 relationship between the path set and observed inputs. /// /// Node notation: /// [id] "{name}":"{value}" /// Tree notation: /// {parentNode} /// {childNode} /// /// Start with the following subtrees: /// /// From strong fingerprint /// /// [1] "PathSet":"VSO0:7E2E49845EC0AE7413519E3EE605272078AF0B1C2911C021681D1D9197CC134A00" /// [2] "ObservedInputs":"" /// [3] "E":"VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00", /// /// From path set hash /// /// [4] "PathSet":"" /// [5] "Path":"B:/out/objects/n/x/qbkexxlc8je93wycw7yrlw0a305n7k/xunit-out/CacheMissAnaAD836B23/3/obj/readonly/src_0" /// [6] "Flags":"IsDirectoryPath, DirectoryEnumeration, DirectoryEnumerationWithAllPattern" /// [7] "EnumeratePatternRegex":"^.*$" /// /// And end with: /// /// [1] "PathSet":"VSO0:7E2E49845EC0AE7413519E3EE605272078AF0B1C2911C021681D1D9197CC134A00" /// [5'] "B:/out/objects/n/x/qbkexxlc8je93wycw7yrlw0a305n7k/xunit-out/CacheMissAnaAD836B23/3/obj/readonly/src_0":"" /// [6'] "Flags":"IsDirectoryPath, DirectoryEnumeration, DirectoryEnumerationWithAllPattern" /// [7'] "EnumeratePatternRegex":"^.*$" /// [3'] "ObservedInput":"E:VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" /// [8] "Members":"[src_1, src_2]" /// </summary> /// <returns> /// The root node of the merged tree (which will be the strong fingerprint tree's root). /// </returns> private JsonNode MergeStrongFingerprintAndPathSetTrees(JsonNode strongFingerprintTree, JsonNode pathSetTree) { // [1] "PathSet":"VSO0:7E2E49845EC0AE7413519E3EE605272078AF0B1C2911C021681D1D9197CC134A00") var parentPathNode = JsonTree.FindNodeByName(strongFingerprintTree, ObservedPathEntryConstants.PathSet); // [2] "ObservedInputs":"" var observedInputsNode = JsonTree.FindNodeByName(strongFingerprintTree, ObservedInputConstants.ObservedInputs); JsonTree.EmancipateBranch(observedInputsNode); // In preparation for merging with observed inputs nodes, // remove the path set node's branch from the path set tree // [4] "PathSet":"" var pathSetNode = JsonTree.FindNodeByName(pathSetTree, ObservedPathEntryConstants.PathSet); JsonTree.EmancipateBranch(pathSetNode); JsonNode currPathNode = null; JsonNode currFlagNode = null; JsonNode currRegexNode = null; var observedInputIt = observedInputsNode.Children.First; for (var it = pathSetNode.Children.First; it != null; it = pathSetNode.Children.First) { var child = it.Value; switch (child.Name) { case ObservedPathEntryConstants.Path: currPathNode = child; // Switch from literal string "path" to actual file system path // [5'] "B:/out/objects/n/x/qbkexxlc8je93wycw7yrlw0a305n7k/xunit-out/CacheMissAnaAD836B23/3/obj/readonly/src_0":"" currPathNode.Name = currPathNode.Values[0]; // The name captures the node's value, so clear the values to avoid extraneous value comparison when diffing currPathNode.Values.Clear(); JsonTree.ReparentBranch(currPathNode, parentPathNode); // [6'] "Flags":"IsDirectoryPath, DirectoryEnumeration, DirectoryEnumerationWithAllPattern" JsonTree.ReparentBranch(currFlagNode, currPathNode); // [7'] "EnumeratePatternRegex":"^.*$" JsonTree.ReparentBranch(currRegexNode, currPathNode); // [3'] "ObservedInput":"E:VSO0:E0C5007DC8CF2D331236F156F136C50CACE2A5D549CD132D9B44ABD1F13D50CC00" // [8] "Members":"[src_1, src_2]" ReparentObservedInput(observedInputIt.Value, currPathNode); observedInputIt = observedInputsNode.Children.First; break; case ObservedPathEntryConstants.Flags: // [6] "Flags":"IsDirectoryPath, DirectoryEnumeration, DirectoryEnumerationWithAllPattern" currFlagNode = child; JsonTree.EmancipateBranch(currFlagNode); break; case ObservedPathEntryConstants.EnumeratePatternRegex: // [7] "EnumeratePatternRegex":"^.*$" currRegexNode = child; JsonTree.EmancipateBranch(currRegexNode); break; default: break; } } // Re-parent any other branches of the path set tree to the strong fingerprint tree // so they are still in a full strong fingerprint tree comparison. // We re-parent under parentPathNode because branches of pathSetTree are elements of PathSet var node = pathSetTree.Children.First; while (node != null) { JsonTree.ReparentBranch(node.Value, parentPathNode); node = pathSetTree.Children.First; } return(strongFingerprintTree); }