Skip to content

Commit

Permalink
Fix case where we incorrectly select a prefix as a FindFirstChar opti…
Browse files Browse the repository at this point in the history
…mization when the pattern includdes an alternation that could lead to different prefixes. (#63976)
  • Loading branch information
joperezr authored Jan 19, 2022
1 parent 454ac0a commit fc3875f
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,55 +79,6 @@ static bool Process(RegexNode node, ref ValueStringBuilder vsb)
return !rtl;
}

// Alternation: find a string that's a shared prefix of all branches
case RegexNode.Alternate:
{
int childCount = node.ChildCount();

// Store the initial branch into the target builder
int initialLength = vsb.Length;
bool keepExploring = Process(node.Child(0), ref vsb);
int addedLength = vsb.Length - initialLength;

// Then explore the rest of the branches, finding the length
// a prefix they all share in common with the initial branch.
if (addedLength != 0)
{
var alternateSb = new ValueStringBuilder(64);

// Process each branch. If we reach a point where we've proven there's
// no overlap, we can bail early.
for (int i = 1; i < childCount && addedLength != 0; i++)
{
alternateSb.Length = 0;

// Process the branch. We want to keep exploring after this alternation,
// but we can't if either this branch doesn't allow for it or if the prefix
// supplied by this branch doesn't entirely match all the previous ones.
keepExploring &= Process(node.Child(i), ref alternateSb);
keepExploring &= alternateSb.Length == addedLength;

addedLength = Math.Min(addedLength, alternateSb.Length);
for (int j = 0; j < addedLength; j++)
{
if (vsb[initialLength + j] != alternateSb[j])
{
addedLength = j;
keepExploring = false;
break;
}
}
}

alternateSb.Dispose();

// Then cull back on what was added based on the other branches.
vsb.Length = initialLength + addedLength;
}

return !rtl && keepExploring;
}

// One character
case RegexNode.One when (node.Options & RegexOptions.IgnoreCase) == 0:
vsb.Append(node.Ch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ public static IEnumerable<object[]> Match_MemberData()
yield return (@"((\d{2,3}?)){2}", "1234", RegexOptions.None, 0, 4, true, "1234");
yield return (@"(abc\d{2,3}?){2}", "abc123abc4567", RegexOptions.None, 0, 12, true, "abc123abc45");

// Testing selected FindOptimizations finds the right prefix
yield return (@"(^|a+)bc", " aabc", RegexOptions.None, 0, 5, true, "aabc");
yield return (@"(^|($|a+))bc", " aabc", RegexOptions.None, 0, 5, true, "aabc");
yield return (@"yz(^|a+)bc", " yzaabc", RegexOptions.None, 0, 7, true, "yzaabc");
yield return (@"(^a|a$) bc", "a bc", RegexOptions.None, 0, 4, true, "a bc");

if (!RegexHelpers.IsNonBacktracking(engine))
{
// Back references not support with NonBacktracking
Expand Down

0 comments on commit fc3875f

Please sign in to comment.