void InsertNode(PathTreeNode node, PathTreeNode parentNode, PathTreeNode previousNode) { parentNode.ChildrenCount += 1; node.Parent = parentNode; if (previousNode == null) { // We're inserting at the beginning. var insertBefore = parentNode.FirstChild; node.Next = insertBefore; if (insertBefore != null) { insertBefore.Previous = node; } parentNode.FirstChild = node; return; } // We are appending inbetween other nodes var next = previousNode.Next; previousNode.Next = node; node.Previous = previousNode; node.Next = next; if (next != null) { next.Previous = node; } }
static void PrettyPrint(StringBuilder builder, PathTreeNode node, string indent) { builder.Append(indent); if (node.Next == null) { builder.Append("\\-"); indent += " "; } else { builder.Append("|-"); indent += "| "; } builder.Append(node.Segment); if (node.IdCount != 0) { builder.AppendFormat(" ({0})", node.IdCount); } node = node.FirstChild; while (node != null) { builder.AppendLine(); PrettyPrint(builder, node, indent); node = node.Next; } }
bool IsDeadSubtree(PathTreeNode node) { // We do a DFS here, looking for any live node in a tree. // We know that leaves are live, so DFS works better here. var stack = new Stack <PathTreeNode> (); stack.Push(node); while (stack.Count != 0) { node = stack.Pop(); if (node.IsLive) { return(false); } var child = node.FirstChild; while (child != null) { stack.Push(child); child = child.Next; } } return(true); }
internal static (PathTreeNode root, PathTreeNode leaf) CreateSubTree(string path, int start) { PathTreeNode lastNode = null, rootNode = null; while (start < path.Length) { var nextSep = path.IndexOf(Path.DirectorySeparatorChar, start); int length = nextSep == -1 ? path.Length - start : nextSep - start; if (length != 0) { var node = new PathTreeNode(path, start, length); if (lastNode != null) { lastNode.FirstChild = node; node.Parent = lastNode; lastNode.ChildrenCount = 1; } else { rootNode = node; } lastNode = node; } start = start + length + 1; } return(rootNode, lastNode); }
bool TryFind(string path, out PathTreeNode result, out PathTreeNode parent, out PathTreeNode previousNode, out int lastIndex) { lastIndex = 0; parent = rootNode; var currentNode = parent.FirstChild; previousNode = null; var remainingSegments = path.AsSpan().TrimEnd(Path.DirectorySeparatorChar); while (currentNode != null) { var currentSegment = currentNode.GetSegment(); // Chunk by directory separator int currentIndex = remainingSegments.IndexOf(Path.DirectorySeparatorChar); int segmentLength = currentIndex == -1 ? remainingSegments.Length : currentIndex; var toAddSegment = remainingSegments.Slice(0, segmentLength); int comparisonResult = currentSegment.CompareTo(toAddSegment, FilePath.PathComparison); // We need to insert in this node's position. if (comparisonResult > 0) { break; } // Keep searching if we still have items. if (comparisonResult < 0) { previousNode = currentNode; currentNode = currentNode.Next; continue; } // We found this segment in the tree. remainingSegments = remainingSegments.Slice(Math.Min(currentIndex + 1, remainingSegments.Length)); lastIndex += currentIndex + 1; // We found the node already, register the ID. if (currentIndex == -1 || remainingSegments.Length == 0) { result = currentNode; return(true); } // We go to the first child of this segment and repeat the algorithm. parent = currentNode; previousNode = null; currentNode = parent.FirstChild; } result = null; return(false); }
public PathTree() { rootNode = new PathTreeNode("", 0, 1); if (!Platform.IsWindows) { rootNode.FirstChild = new PathTreeNode("/", 0, 0) { Parent = rootNode, }; rootNode.ChildrenCount = 1; } }
bool TryFind(string path, out PathTreeNode result, out PathTreeNode parent, out PathTreeNode previousNode, out int lastIndex) { lastIndex = 0; parent = rootNode; var currentNode = parent.FirstChild; previousNode = null; while (currentNode != null) { int currentIndex = path.IndexOf(Path.DirectorySeparatorChar, lastIndex); int comparisonResult = string.Compare(currentNode.FullPath, currentNode.Start, path, lastIndex, currentNode.Length); // We need to insert in this node's position. if (comparisonResult > 0) { break; } // Keep searching. if (comparisonResult < 0) { previousNode = currentNode; currentNode = currentNode.Next; continue; } // We found this segment in the tree. lastIndex = currentIndex + 1; // We found the node already, register the ID. if (currentIndex == -1 || lastIndex == path.Length) { result = currentNode; return(true); } // We go to the first child of this segment and repeat the algorithm. parent = currentNode; previousNode = null; currentNode = parent.FirstChild; } result = null; return(false); }
public void CreateSubTree(string sep) { var path = MakePath("a", "b", "c") + sep; var(first, leaf) = PathTreeNode.CreateSubTree(path, 0); PathTreeNode a; if (Platform.IsWindows) { AssertPathTreeSubtree(first, "C:"); Assert.AreEqual(1, first.ChildrenCount); a = first.FirstChild; Assert.AreSame(first, a.Parent); } else { a = first; } AssertPathTreeSubtree(a, "a"); Assert.AreEqual(1, a.ChildrenCount); var b = a.FirstChild; Assert.AreSame(a, b.Parent); AssertPathTreeSubtree(b, "b"); Assert.AreEqual(1, b.ChildrenCount); var c = b.FirstChild; Assert.AreSame(b, c.Parent); AssertPathTreeSubtree(c, "c"); Assert.AreEqual(0, c.ChildrenCount); Assert.AreSame(c, leaf); Assert.IsNull(c.FirstChild); void AssertPathTreeSubtree(PathTreeNode node, string segment) { Assert.AreEqual(segment, node.Segment); Assert.IsNull(node.Next); Assert.AreSame(node.FirstChild, node.LastChild); } }
public PathTreeNode AddNode(string path, object id, out bool isModified) { if (TryFind(path, out var result, out var parent, out var previousNode, out var lastIndex)) { isModified = !result.IsLive; result.RegisterId(id); return(result); } // At this point, we need to create a new node. isModified = true; var(first, leaf) = PathTreeNode.CreateSubTree(path, lastIndex); leaf.RegisterId(id); InsertNode(first, parent, previousNode); return(leaf); }
public PathTreeNode AddNode(string path, object id) { if (TryFind(path, out var result, out var parent, out var previousNode, out var lastIndex)) { result.RegisterId(id); return(result); } // At this point, we need to create a new node. var(first, leaf) = PathTreeNode.CreateSubTree(path, lastIndex); if (id != null) { leaf.RegisterId(id); } InsertNode(first, parent, previousNode); return(leaf); }
internal PathTreeNode RemoveNode(PathTreeNode result, object id, out bool isModified) { var parent = result.Parent; isModified = result.UnregisterId(id) && !result.IsLive; if (isModified) { var nodeToRemove = result; var lastToRemove = pathRoot; while (nodeToRemove != lastToRemove && IsDeadSubtree(nodeToRemove)) { parent.ChildrenCount -= 1; if (parent.FirstChild == nodeToRemove) { parent.FirstChild = nodeToRemove.Next; } if (nodeToRemove.Previous != null) { nodeToRemove.Previous.Next = nodeToRemove.Next; } if (nodeToRemove.Next != null) { nodeToRemove.Next.Previous = nodeToRemove.Previous; } nodeToRemove.Next = null; nodeToRemove.Previous = null; nodeToRemove.Parent = null; nodeToRemove = parent; parent = nodeToRemove.Parent; } } return(result); }
[TestCase(1)] // Should not crash public void EmptySubTrie(int startIndex) { var(node, leaf) = PathTreeNode.CreateSubTree(string.Empty, startIndex); Assert.IsNull(node); Assert.IsNull(leaf); }
public void JustSlash() { var(node, leaf) = PathTreeNode.CreateSubTree(Path.DirectorySeparatorChar.ToString(), 0); Assert.IsNull(node); Assert.IsNull(leaf); }