Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Avoid intermediate string allocations by updating SpanBasedStringBuilder
  • Loading branch information
Erarndt committed May 16, 2025
commit b34eae3515f3db9b11a9ea64b41ad2596a148ee8
25 changes: 11 additions & 14 deletions src/Build/Evaluation/Expander.cs
Original file line number Diff line number Diff line change
Expand Up @@ -779,37 +779,34 @@ private static void AddArgument(List<string> arguments, SpanBasedStringBuilder a
// we reached the end of an argument, add the builder's final result
// to our arguments.
argumentBuilder.Trim();
string argValue = argumentBuilder.ToString();

// We support passing of null through the argument constant value null
if (String.Equals("null", argValue, StringComparison.OrdinalIgnoreCase))
if (argumentBuilder.Equals("null", StringComparison.OrdinalIgnoreCase))
{
arguments.Add(null);
}
else
{
if (argValue.Length > 0)
if (argumentBuilder.Length > 0)
{
if (argValue[0] == '\'' && argValue[argValue.Length - 1] == '\'')
if (argumentBuilder[0] == '\'' && argumentBuilder[argumentBuilder.Length - 1] == '\'')
{
arguments.Add(argValue.Trim(s_singleQuoteChar));
argumentBuilder.Trim('\'');
}
else if (argValue[0] == '`' && argValue[argValue.Length - 1] == '`')
else if (argumentBuilder[0] == '`' && argumentBuilder[argumentBuilder.Length - 1] == '`')
{
arguments.Add(argValue.Trim(s_backtickChar));
argumentBuilder.Trim('`');
}
else if (argValue[0] == '"' && argValue[argValue.Length - 1] == '"')
else if (argumentBuilder[0] == '"' && argumentBuilder[argumentBuilder.Length - 1] == '"')
{
arguments.Add(argValue.Trim(s_doubleQuoteChar));
}
else
{
arguments.Add(argValue);
argumentBuilder.Trim('"');
}

arguments.Add(argumentBuilder.ToString());
}
else
{
arguments.Add(argValue);
arguments.Add(string.Empty);
}
}
}
Expand Down
97 changes: 97 additions & 0 deletions src/StringTools/SpanBasedStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,33 @@ public SpanBasedStringBuilder(int capacity = 4)
/// </summary>
public int Capacity => _spans.Capacity;

public char this[int index]
{
get
{
if (index < 0 || index >= Length)
{
throw new ArgumentOutOfRangeException(nameof(index));
}

int adjustedIndex = index;
foreach (ReadOnlyMemory<char> span in _spans)
{
if (adjustedIndex < span.Length)
{
return span.Span[adjustedIndex];
}
else
{
adjustedIndex -= span.Length;
}
}

// Should never reach here if Length is correct
throw new IndexOutOfRangeException();
}
}

/// <summary>
/// Creates a new enumerator for enumerating characters in this string. Does not allocate.
/// </summary>
Expand All @@ -129,6 +156,26 @@ public Enumerator GetEnumerator()
return new Enumerator(_spans);
}

public bool Equals(string other, StringComparison comparison)
{
if (other.Length != Length)
{
return false;
}

int index = 0;
ReadOnlySpan<char> inputSpan = other.AsSpan();
foreach (ReadOnlyMemory<char> memory in _spans)
{
if (!MemoryExtensions.Equals(memory.Span, inputSpan.Slice(index, memory.Length), comparison))
{
return false;
}
}

return true;
}

/// <summary>
/// Converts this instance to a System.String while first searching for a match in the intern table.
/// </summary>
Expand Down Expand Up @@ -226,6 +273,28 @@ public void TrimStart()
}
}

public void TrimStart(char c)
{
for (int spanIdx = 0; spanIdx < _spans.Count; spanIdx++)
{
ReadOnlySpan<char> span = _spans[spanIdx].Span;
int i = 0;
while (i < span.Length && span[i] == c)
{
i++;
}
if (i > 0)
{
_spans[spanIdx] = _spans[spanIdx].Slice(i);
Length -= i;
}
if (!_spans[spanIdx].IsEmpty)
{
return;
}
}
}

/// <summary>
/// Removes trailing white-space characters from the string.
/// </summary>
Expand All @@ -251,6 +320,28 @@ public void TrimEnd()
}
}

public void TrimEnd(char c)
{
for (int spanIdx = _spans.Count - 1; spanIdx >= 0; spanIdx--)
{
ReadOnlySpan<char> span = _spans[spanIdx].Span;
int i = span.Length - 1;
while (i >= 0 && span[i] == c)
{
i--;
}
if (i + 1 < span.Length)
{
_spans[spanIdx] = _spans[spanIdx].Slice(0, i + 1);
Length -= span.Length - (i + 1);
}
if (!_spans[spanIdx].IsEmpty)
{
return;
}
}
}

/// <summary>
/// Removes leading and trailing white-space characters from the string.
/// </summary>
Expand All @@ -260,6 +351,12 @@ public void Trim()
TrimEnd();
}

public void Trim(char c)
{
TrimStart(c);
TrimEnd(c);
}

/// <summary>
/// Clears this instance making it represent an empty string.
/// </summary>
Expand Down