Skip to content

Commit 721ed1a

Browse files
Copilotbaronfel
andcommitted
Add fast-path for Char.IsDigit in WellKnownFunctions
Co-authored-by: baronfel <[email protected]>
1 parent 806731d commit 721ed1a

File tree

5 files changed

+50
-30
lines changed

5 files changed

+50
-30
lines changed

src/Build.UnitTests/Evaluation/Expander_Tests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4741,6 +4741,40 @@ public void PropertyFunctionStringArrayGetValue()
47414741
TestPropertyFunction("$(X.Split($([System.Convert]::ToString(`.`).ToCharArray())).GetValue($([System.Convert]::ToInt32(0))))", "X", "ab.cd", "ab");
47424742
}
47434743

4744+
/// <summary>
4745+
/// Test that Char.IsDigit fast-path works correctly
4746+
/// </summary>
4747+
[Fact]
4748+
public void PropertyFunctionCharIsDigit()
4749+
{
4750+
PropertyDictionary<ProjectPropertyInstance> pg = new PropertyDictionary<ProjectPropertyInstance>();
4751+
4752+
Expander<ProjectPropertyInstance, ProjectItemInstance> expander = new Expander<ProjectPropertyInstance, ProjectItemInstance>(pg, FileSystems.Default);
4753+
4754+
// Test with digit characters
4755+
string result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit('5'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4756+
Assert.Equal("True", result);
4757+
4758+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit('0'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4759+
Assert.Equal("True", result);
4760+
4761+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit('9'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4762+
Assert.Equal("True", result);
4763+
4764+
// Test with non-digit characters
4765+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit('a'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4766+
Assert.Equal("False", result);
4767+
4768+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit(' '))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4769+
Assert.Equal("False", result);
4770+
4771+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit('/'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4772+
Assert.Equal("False", result);
4773+
4774+
result = expander.ExpandIntoStringLeaveEscaped("$([System.Char]::IsDigit(':'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
4775+
Assert.Equal("False", result);
4776+
}
4777+
47444778
private void TestPropertyFunction(string expression, string propertyName, string propertyValue, string expected)
47454779
{
47464780
var properties = new PropertyDictionary<ProjectPropertyInstance>();

src/Build.UnitTests/Scanner_Tests.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -562,26 +562,5 @@ public void NegativeTests()
562562
Scanner lexer = new Scanner("'$(DEBUG) == true", ParserOptions.AllowAll);
563563
Assert.False(lexer.Advance());
564564
}
565-
566-
/// <summary>
567-
/// Tests the fast-path IsDigit method in CharacterUtilities.
568-
/// </summary>
569-
[Fact]
570-
public void CharacterUtilities_IsDigit_WorksCorrectly()
571-
{
572-
// Test ASCII digits
573-
for (char c = '0'; c <= '9'; c++)
574-
{
575-
Assert.True(CharacterUtilities.IsDigit(c), $"Character '{c}' should be identified as a digit");
576-
}
577-
578-
// Test non-digit characters
579-
Assert.False(CharacterUtilities.IsDigit('/'), "Character '/' should not be identified as a digit");
580-
Assert.False(CharacterUtilities.IsDigit(':'), "Character ':' should not be identified as a digit");
581-
Assert.False(CharacterUtilities.IsDigit('a'), "Character 'a' should not be identified as a digit");
582-
Assert.False(CharacterUtilities.IsDigit('A'), "Character 'A' should not be identified as a digit");
583-
Assert.False(CharacterUtilities.IsDigit(' '), "Character ' ' should not be identified as a digit");
584-
Assert.False(CharacterUtilities.IsDigit('\0'), "Character '\\0' should not be identified as a digit");
585-
}
586565
}
587566
}

src/Build/Evaluation/Conditionals/CharacterUtilities.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal static class CharacterUtilities
99
{
1010
internal static bool IsNumberStart(char candidate)
1111
{
12-
return candidate == '+' || candidate == '-' || candidate == '.' || IsDigit(candidate);
12+
return candidate == '+' || candidate == '-' || candidate == '.' || char.IsDigit(candidate);
1313
}
1414

1515
internal static bool IsSimpleStringStart(char candidate)
@@ -19,17 +19,12 @@ internal static bool IsSimpleStringStart(char candidate)
1919

2020
internal static bool IsSimpleStringChar(char candidate)
2121
{
22-
return IsSimpleStringStart(candidate) || IsDigit(candidate);
23-
}
24-
25-
internal static bool IsDigit(char candidate)
26-
{
27-
return candidate >= '0' && candidate <= '9';
22+
return IsSimpleStringStart(candidate) || char.IsDigit(candidate);
2823
}
2924

3025
internal static bool IsHexDigit(char candidate)
3126
{
32-
return IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a');
27+
return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a');
3328
}
3429
}
3530
}

src/Build/Evaluation/Conditionals/Scanner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ private void SkipWhiteSpace()
772772
}
773773
private void SkipDigits()
774774
{
775-
while (_parsePoint < _expression.Length && CharacterUtilities.IsDigit(_expression[_parsePoint]))
775+
while (_parsePoint < _expression.Length && char.IsDigit(_expression[_parsePoint]))
776776
{
777777
_parsePoint++;
778778
}

src/Build/Evaluation/Expander/WellKnownFunctions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,18 @@ internal static bool TryExecuteWellKnownFunction(string methodName, Type receive
875875
}
876876
}
877877
}
878+
else if (receiverType == typeof(char))
879+
{
880+
if (string.Equals(methodName, nameof(char.IsDigit), StringComparison.OrdinalIgnoreCase))
881+
{
882+
if (ParseArgs.TryGetArg(args, out string? arg0) && arg0?.Length == 1)
883+
{
884+
char c = arg0[0];
885+
returnVal = c >= '0' && c <= '9';
886+
return true;
887+
}
888+
}
889+
}
878890
else if (string.Equals(methodName, nameof(Regex.Replace), StringComparison.OrdinalIgnoreCase) && args.Length == 3)
879891
{
880892
if (ParseArgs.TryGetArgs(args, out string? arg1, out string? arg2, out string? arg3) && arg1 != null && arg2 != null && arg3 != null)

0 commit comments

Comments
 (0)