提交 2ee11747 编写于 作者: C CyrusNajmabadi

Provide non-allocating interval tree helpers.

上级 8e26fb96
......@@ -220,11 +220,11 @@ public void TestIntersectsWith()
foreach (var tree in CreateTrees(spans))
{
Assert.False(tree.IntersectsWith(-1));
Assert.True(tree.IntersectsWith(0));
Assert.True(tree.IntersectsWith(1));
Assert.True(tree.IntersectsWith(2));
Assert.False(tree.IntersectsWith(3));
Assert.False(tree.HasIntervalThatIntersectsWith(-1));
Assert.True(tree.HasIntervalThatIntersectsWith(0));
Assert.True(tree.HasIntervalThatIntersectsWith(1));
Assert.True(tree.HasIntervalThatIntersectsWith(2));
Assert.False(tree.HasIntervalThatIntersectsWith(3));
}
}
......
......@@ -173,7 +173,7 @@ public override SyntaxNode Visit(SyntaxNode node)
{
_cancellationToken.ThrowIfCancellationRequested();
if (node == null || !_spans.IntersectsWith(node.FullSpan))
if (node == null || !_spans.HasIntervalThatIntersectsWith(node.FullSpan))
{
return node;
}
......@@ -185,7 +185,7 @@ public override SyntaxToken VisitToken(SyntaxToken token)
{
_cancellationToken.ThrowIfCancellationRequested();
if (!_spans.IntersectsWith(token.FullSpan))
if (!_spans.HasIntervalThatIntersectsWith(token.FullSpan))
{
return token;
}
......
......@@ -331,12 +331,14 @@ private IEnumerable<TextSpan> GetNonOverlappingSpans(ISyntaxFactsService syntaxF
// Make sure the previous and next tokens we found do not overlap with any existing spans. If they do, merge two spans.
previousToken = (previousToken.RawKind == 0) ? root.GetFirstToken(includeZeroWidth: true) : previousToken;
var start = intervalTree.GetOverlappingIntervals(previousToken.SpanStart, previousToken.Span.Length).Any() ?
previousToken.SpanStart : startToken.SpanStart;
var start = intervalTree.HasIntervalThatOverlapsWith(previousToken.SpanStart, previousToken.Span.Length)
? previousToken.SpanStart
: startToken.SpanStart;
nextToken = (nextToken.RawKind == 0) ? root.GetLastToken(includeZeroWidth: true) : nextToken;
var end = intervalTree.GetOverlappingIntervals(nextToken.SpanStart, nextToken.Span.Length).Any() ?
nextToken.Span.End : endToken.Span.End;
var end = intervalTree.HasIntervalThatOverlapsWith(nextToken.SpanStart, nextToken.Span.Length)
? nextToken.Span.End
: endToken.Span.End;
tokenSpans.Add(TextSpan.FromBounds(start, end));
}
......
......@@ -31,7 +31,7 @@ internal abstract class ImportAdderService : ILanguageService
var spansTree = new SimpleIntervalTree<TextSpan>(TextSpanIntervalIntrospector.Instance, spans);
Func<SyntaxNodeOrToken, bool> isInSpan = nodeOrToken =>
spansTree.GetOverlappingIntervals(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length).Any();
spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length);
var nodesWithExplicitNamespaces = root.DescendantNodesAndSelf().Where(n => isInSpan(n) && GetExplicitNamespaceSymbol(n, model) != null).ToList();
......
......@@ -381,7 +381,7 @@ public void AddAnchorIndentationOperation(AnchorIndentationOperation operation)
private void DebugCheckEmpty<T>(ContextIntervalTree<T> tree, TextSpan textSpan)
{
var intervals = tree.GetContainingIntervals(textSpan.Start, textSpan.Length);
Contract.ThrowIfFalse(intervals.IsEmpty());
Contract.ThrowIfFalse(intervals.Length == 0);
}
public int GetBaseIndentation(SyntaxToken token)
......
......@@ -76,7 +76,9 @@ private IList<TextChange> CreateTextChanges(CancellationToken cancellationToken)
var changes = CreateTextChangesWorker(cancellationToken);
// formatted spans and formatting spans are different, filter returns to formatting span
return _formattingSpans == null ? changes : changes.Where(s => _formattingSpans.IntersectsWith(s.Span)).ToList();
return _formattingSpans == null
? changes
: changes.Where(s => _formattingSpans.HasIntervalThatIntersectsWith(s.Span)).ToList();
}
}
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Roslyn.Utilities;
......@@ -10,8 +11,9 @@
namespace Microsoft.CodeAnalysis.Shared.Collections
{
/// <summary>
/// An interval tree represents an ordered tree data structure to store intervals of the form [start, end). It
/// allows you to efficiently find all intervals that intersect or overlap a provided interval.
/// An interval tree represents an ordered tree data structure to store intervals of the form
/// [start, end). It allows you to efficiently find all intervals that intersect or overlap
/// a provided interval.
/// </summary>
internal partial class IntervalTree<T> : IEnumerable<T>
{
......@@ -19,10 +21,13 @@ internal partial class IntervalTree<T> : IEnumerable<T>
protected Node root;
private delegate bool TestInterval(T value, int start, int length, IIntervalIntrospector<T> introspector);
private static readonly TestInterval s_intersectsWithTest = IntersectsWith;
private static readonly TestInterval s_containsTest = Contains;
private static readonly TestInterval s_overlapsWithTest = OverlapsWith;
protected delegate bool TestInterval(T value, int start, int length, IIntervalIntrospector<T> introspector);
protected static readonly TestInterval s_intersectsWithTest = IntersectsWith;
protected static readonly TestInterval s_containsTest = Contains;
protected static readonly TestInterval s_overlapsWithTest = OverlapsWith;
private static readonly ObjectPool<Stack<(Node node, bool firstTime)>> s_stackPool =
new ObjectPool<Stack<(Node node, bool firstTime)>>(() => new Stack<(Node node, bool firstTime)>());
public IntervalTree()
{
......@@ -83,37 +88,71 @@ private static bool OverlapsWith(T value, int start, int length, IIntervalIntros
return overlapStart < overlapEnd;
}
public IEnumerable<T> GetOverlappingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
{
return this.GetInOrderIntervals(start, length, s_overlapsWithTest, introspector);
}
public ImmutableArray<T> GetOverlappingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
=> this.GetInOrderIntervals(start, length, s_overlapsWithTest, introspector);
public IEnumerable<T> GetIntersectingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
{
return this.GetInOrderIntervals(start, length, s_intersectsWithTest, introspector);
}
public ImmutableArray<T> GetIntersectingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
=> this.GetInOrderIntervals(start, length, s_intersectsWithTest, introspector);
public ImmutableArray<T> GetContainingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
=> this.GetInOrderIntervals(start, length, s_containsTest, introspector);
public void FillOverlappingIntervals(int start, int length, IIntervalIntrospector<T> introspector, ArrayBuilder<T> builder)
=> this.FillInOrderIntervals(start, length, s_overlapsWithTest, introspector, builder, stopAfterFirst: false);
public void FillIntersectingIntervals(int start, int length, IIntervalIntrospector<T> introspector, ArrayBuilder<T> builder)
=> this.FillInOrderIntervals(start, length, s_intersectsWithTest, introspector, builder, stopAfterFirst: false);
public void FillContainingIntervals(int start, int length, IIntervalIntrospector<T> introspector, ArrayBuilder<T> builder)
=> this.FillInOrderIntervals(start, length, s_containsTest, introspector, builder, stopAfterFirst: false);
public bool IntersectsWith(int position, IIntervalIntrospector<T> introspector)
=> Any(position, 0, s_intersectsWithTest, introspector);
public IEnumerable<T> GetContainingIntervals(int start, int length, IIntervalIntrospector<T> introspector)
protected bool Any(int start, int length, TestInterval testInterval, IIntervalIntrospector<T> introspector)
{
return this.GetInOrderIntervals(start, length, s_containsTest, introspector);
var builder = ArrayBuilder<T>.GetInstance();
FillInOrderIntervals(start, 0, s_intersectsWithTest, introspector, builder, stopAfterFirst: true);
var result = builder.Count > 0;
builder.Free();
return result;
}
public bool IntersectsWith(int position, IIntervalIntrospector<T> introspector)
private ImmutableArray<T> GetInOrderIntervals(int start, int length, TestInterval testInterval, IIntervalIntrospector<T> introspector)
{
return GetIntersectingIntervals(position, 0, introspector).Any();
var result = ArrayBuilder<T>.GetInstance();
FillInOrderIntervals(start, length, testInterval, introspector, result, stopAfterFirst: false);
return result.ToImmutableAndFree();
}
private IEnumerable<T> GetInOrderIntervals(int start, int length, TestInterval testInterval, IIntervalIntrospector<T> introspector)
private void FillInOrderIntervals(
int start, int length, TestInterval testInterval,
IIntervalIntrospector<T> introspector, ArrayBuilder<T> builder,
bool stopAfterFirst)
{
if (root == null)
{
yield break;
return;
}
var candidates = s_stackPool.Allocate();
FillInOrderIntervals(
start, length, testInterval,
introspector, builder, stopAfterFirst, candidates);
s_stackPool.ClearAndFree(candidates);
}
private void FillInOrderIntervals(
int start, int length, TestInterval testInterval,
IIntervalIntrospector<T> introspector, ArrayBuilder<T> builder,
bool stopAfterFirst, Stack<(Node node, bool firstTime)> candidates)
{
var end = start + length;
// The bool indicates if this is the first time we are seeing the node.
var candidates = new Stack<(Node node, bool firstTime)>();
candidates.Push((root, true));
while (candidates.Count > 0)
......@@ -130,7 +169,12 @@ private IEnumerable<T> GetInOrderIntervals(int start, int length, TestInterval t
// side of it). Now see if it matches our test, and if so return it out.
if (testInterval(currentNode.Value, start, length, introspector))
{
yield return currentNode.Value;
builder.Add(currentNode.Value);
if (stopAfterFirst)
{
return;
}
}
}
else
......@@ -165,10 +209,7 @@ private IEnumerable<T> GetInOrderIntervals(int start, int length, TestInterval t
}
}
public bool IsEmpty()
{
return this.root == null;
}
public bool IsEmpty() => this.root == null;
protected static Node Insert(Node root, Node newNode, IIntervalIntrospector<T> introspector)
{
......@@ -242,7 +283,6 @@ public IEnumerator<T> GetEnumerator()
yield break;
}
// The bool indicates if this is the first time we are seeing the node.
var candidates = new Stack<(Node node, bool firstTime)>();
candidates.Push((root, firstTime: true));
while (candidates.Count != 0)
......@@ -269,33 +309,18 @@ public IEnumerator<T> GetEnumerator()
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
=> this.GetEnumerator();
protected static int GetEnd(T value, IIntervalIntrospector<T> introspector)
{
return introspector.GetStart(value) + introspector.GetLength(value);
}
=> introspector.GetStart(value) + introspector.GetLength(value);
protected static int MaxEndValue(Node node, IIntervalIntrospector<T> arg)
{
return node == null ? 0 : GetEnd(node.MaxEndNode.Value, arg);
}
=> node == null ? 0 : GetEnd(node.MaxEndNode.Value, arg);
private static int Height(Node node)
{
return node == null ? 0 : node.Height;
}
=> node == null ? 0 : node.Height;
private static int BalanceFactor(Node node)
{
if (node == null)
{
return 0;
}
return Height(node.Left) - Height(node.Right);
}
=> node == null ? 0 : Height(node.Left) - Height(node.Right);
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Shared.Collections
{
......@@ -24,29 +24,37 @@ public SimpleIntervalTree(IIntervalIntrospector<T> introspector, IEnumerable<T>
protected IIntervalIntrospector<T> Introspector => _introspector;
public IEnumerable<T> GetOverlappingIntervals(int start, int length)
{
return GetOverlappingIntervals(start, length, _introspector);
}
public ImmutableArray<T> GetOverlappingIntervals(int start, int length)
=> GetOverlappingIntervals(start, length, _introspector);
public IEnumerable<T> GetIntersectingIntervals(int start, int length)
{
return GetIntersectingIntervals(start, length, _introspector);
}
public ImmutableArray<T> GetIntersectingIntervals(int start, int length)
=> GetIntersectingIntervals(start, length, _introspector);
public IEnumerable<T> GetContainingIntervals(int start, int length)
{
return GetContainingIntervals(start, length, _introspector);
}
public ImmutableArray<T> GetContainingIntervals(int start, int length)
=> GetContainingIntervals(start, length, _introspector);
public bool IntersectsWith(int position)
{
return GetIntersectingIntervals(position, 0).Any();
}
public void FillOverlappingIntervals(int start, int length, ArrayBuilder<T> builder)
=> FillOverlappingIntervals(start, length, _introspector, builder);
public void FillIntersectingIntervals(int start, int length, ArrayBuilder<T> builder)
=> FillIntersectingIntervals(start, length, _introspector, builder);
public void FillContainingIntervals(int start, int length, ArrayBuilder<T> builder)
=> FillContainingIntervals(start, length, _introspector, builder);
public bool HasIntervalThatIntersectsWith(int position)
=> HasIntervalThatIntersectsWith(position, 0);
public bool HasIntervalThatOverlapsWith(int start, int length)
=> Any(start, length, s_overlapsWithTest, _introspector);
public bool HasIntervalThatIntersectsWith(int start, int length)
=> Any(start, length, s_intersectsWithTest, _introspector);
public bool HasIntervalThatContains(int start, int length)
=> Any(start, length, s_containsTest, _introspector);
protected int MaxEndValue(Node node)
{
return GetEnd(node.MaxEndNode.Value, _introspector);
}
=> GetEnd(node.MaxEndNode.Value, _introspector);
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;
......@@ -12,9 +10,7 @@ internal static class SimpleIntervalTreeExtensions
/// <summary>
/// check whether the given span is intersects with the tree
/// </summary>
public static bool IntersectsWith(this SimpleIntervalTree<TextSpan> tree, TextSpan span)
{
return tree.GetIntersectingIntervals(span.Start, span.Length).Any();
}
public static bool HasIntervalThatIntersectsWith(this SimpleIntervalTree<TextSpan> tree, TextSpan span)
=> tree.HasIntervalThatIntersectsWith(span.Start, span.Length);
}
}
}
\ No newline at end of file
......@@ -99,7 +99,7 @@ protected virtual SyntaxNode TransformReducedNode(SyntaxNode reducedNode, Syntax
var spansTree = new SimpleIntervalTree<TextSpan>(TextSpanIntervalIntrospector.Instance, spans);
Func<SyntaxNodeOrToken, bool> isNodeOrTokenOutsideSimplifySpans = (nodeOrToken) =>
!spansTree.GetOverlappingIntervals(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length).Any();
!spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
......
......@@ -328,7 +328,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
Dim span = originalNode.Span
If syntaxPredicate() AndAlso
_spans.GetContainingIntervals(span.Start, span.Length).Any() AndAlso
_spans.HasIntervalThatContains(span.Start, span.Length) AndAlso
CheckSkippedTriviaForMissingToken(originalNode, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken) Then
Dim transformedNode = transform(DirectCast(node, T))
......@@ -445,7 +445,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
End If
Dim span = originalToken.Span
If Not _spans.GetContainingIntervals(span.Start, span.Length).Any() Then
If Not _spans.HasIntervalThatContains(span.Start, span.Length) Then
' token is outside of the provided span
Return token
End If
......
......@@ -76,7 +76,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
' if there are no overlapping spans, no need to walk down this node
If node Is Nothing OrElse
Not _spans.GetOverlappingIntervals(node.FullSpan.Start, node.FullSpan.Length).Any() Then
Not _spans.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) Then
Return node
End If
......@@ -186,7 +186,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
Dim visitedNode = DirectCast(MyBase.VisitOperatorStatement(node), OperatorStatementSyntax)
Dim span = node.Span
If Not _spans.GetContainingIntervals(span.Start, span.Length).Any() Then
If Not _spans.HasIntervalThatContains(span.Start, span.Length) Then
Return visitedNode
End If
......@@ -226,7 +226,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
End If
Dim span = node.Span
If Not _spans.GetContainingIntervals(span.Start, span.Length).Any() Then
If Not _spans.HasIntervalThatContains(span.Start, span.Length) Then
Return binaryOperator
End If
......@@ -255,7 +255,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
Dim newToken = MyBase.VisitToken(token)
Dim span = token.Span
If Not _spans.GetContainingIntervals(span.Start, span.Length).Any() Then
If Not _spans.HasIntervalThatContains(span.Start, span.Length) Then
Return newToken
End If
......@@ -438,7 +438,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
' whole node must be under span, otherwise, we will just return
Dim span = originalNode.Span
If Not _spans.GetContainingIntervals(span.Start, span.Length).Any() Then
If Not _spans.HasIntervalThatContains(span.Start, span.Length) Then
Return node
End If
......
......@@ -132,7 +132,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
If node Is Nothing OrElse Not Me._spans.IntersectsWith(node.FullSpan) Then
If node Is Nothing OrElse Not Me._spans.HasIntervalThatIntersectsWith(node.FullSpan) Then
Return node
End If
......@@ -142,7 +142,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
Public Overrides Function VisitToken(token As SyntaxToken) As SyntaxToken
_cancellationToken.ThrowIfCancellationRequested()
If Not Me._spans.IntersectsWith(token.FullSpan) Then
If Not Me._spans.HasIntervalThatIntersectsWith(token.FullSpan) Then
Return token
End If
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册