File: EscapingUtilitiesBenchmark.cs
Web Access
Project: ..\..\..\src\MSBuild.Benchmarks\MSBuild.Benchmarks.csproj (MSBuild.Benchmarks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using BenchmarkDotNet.Attributes;
using Microsoft.Build.Shared;
 
namespace MSBuild.Benchmarks;
 
[MemoryDiagnoser]
public class EscapingUtilitiesBenchmark
{
    /// <summary>
    /// A typical file path with no special characters — the most common fast path.
    /// </summary>
    private const string NoSpecialChars = @"C:\repos\msbuild\src\Framework\EscapingUtilities.cs";
 
    /// <summary>
    /// A string with a few characters that need escaping (semicolons, parens, percent).
    /// Represents a realistic property or item value.
    /// </summary>
    private const string FewSpecialChars = @"Reference=$(PkgPath);Version=1.0.0";
 
    /// <summary>
    /// A string where most characters are escapable — worst case for escaping.
    /// </summary>
    private const string ManySpecialChars = @"%;*?@$();'%;*?@$();'%;*?@$();'";
 
    /// <summary>
    /// An already-escaped string with a few %XX sequences — common unescape input.
    /// </summary>
    private const string FewEscapeSequences = @"Reference%3d%24%28PkgPath%29%3BVersion%3d1.0.0";
 
    /// <summary>
    /// A heavily-escaped string — worst case for unescaping.
    /// </summary>
    private const string ManyEscapeSequences = @"%25%3b%2a%3f%40%24%28%29%3b%27%25%3b%2a%3f%40%24%28%29%3b%27";
 
    /// <summary>
    /// A string with partial/invalid escape sequences that must be skipped.
    /// </summary>
    private const string InvalidEscapeSequences = @"100%done%Z%2%";
 
    /// <summary>
    /// An escaped string with leading/trailing whitespace for the trim path.
    /// </summary>
    private const string EscapedWithWhitespace = @"   foo%20bar%3Bbaz   ";
 
    /// <summary>
    /// An escaped string containing wildcard escape sequences (%2a, %3f).
    /// </summary>
    private const string EscapedWildcards = @"src\**\%2a.cs%3f";
 
    /// <summary>
    /// A long escaped string without wildcard sequences — worst case scan for ContainsEscapedWildcards.
    /// </summary>
    private const string LongNoWildcards = @"abcdefghijklmnopqrstuvwxyz%3babcdefghijklmnopqrstuvwxyz%3babcdefghijklmnopqrstuvwxyz%3babcdefghijklmnopqrstuvwxyz";
 
    // --- UnescapeAll ---
 
    [Benchmark]
    public string UnescapeAll_NoSpecialChars()
        => EscapingUtilities.UnescapeAll(NoSpecialChars);
 
    [Benchmark]
    public string UnescapeAll_FewEscapeSequences()
        => EscapingUtilities.UnescapeAll(FewEscapeSequences);
 
    [Benchmark]
    public string UnescapeAll_ManyEscapeSequences()
        => EscapingUtilities.UnescapeAll(ManyEscapeSequences);
 
    [Benchmark]
    public string UnescapeAll_InvalidEscapeSequences()
        => EscapingUtilities.UnescapeAll(InvalidEscapeSequences);
 
    [Benchmark]
    public string UnescapeAll_WithTrim()
        => EscapingUtilities.UnescapeAll(EscapedWithWhitespace, trim: true);
 
    // --- Escape ---
 
    [Benchmark]
    public string Escape_NoSpecialChars()
        => EscapingUtilities.Escape(NoSpecialChars);
 
    [Benchmark]
    public string Escape_FewSpecialChars()
        => EscapingUtilities.Escape(FewSpecialChars);
 
    [Benchmark]
    public string Escape_ManySpecialChars()
        => EscapingUtilities.Escape(ManySpecialChars);
 
    // --- EscapeWithCaching ---
 
    [Benchmark]
    public string EscapeWithCaching_FewSpecialChars()
        => EscapingUtilities.Escape(FewSpecialChars, cache: true);
 
    [Benchmark]
    public string EscapeWithCaching_ManySpecialChars()
        => EscapingUtilities.Escape(ManySpecialChars, cache: true);
 
    // --- ContainsEscapedWildcards ---
 
    [Benchmark]
    public bool ContainsEscapedWildcards_NoPercent()
        => EscapingUtilities.ContainsEscapedWildcards(NoSpecialChars);
 
    [Benchmark]
    public bool ContainsEscapedWildcards_HasWildcards()
        => EscapingUtilities.ContainsEscapedWildcards(EscapedWildcards);
 
    [Benchmark]
    public bool ContainsEscapedWildcards_LongNoWildcards()
        => EscapingUtilities.ContainsEscapedWildcards(LongNoWildcards);
 
    // --- Round-trip ---
 
    [Benchmark]
    public string RoundTrip_EscapeThenUnescape()
        => EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(FewSpecialChars));
}