Skip to content

Commit

Permalink
Add Expander<P,I>.SpanBasedConcatenator
Browse files Browse the repository at this point in the history
  • Loading branch information
ladipro committed Feb 7, 2021
1 parent a6ec133 commit 1dd1c62
Showing 1 changed file with 117 additions and 0 deletions.
117 changes: 117 additions & 0 deletions src/Build/Evaluation/Expander.cs
Expand Up @@ -123,6 +123,123 @@ internal class Expander<P, I>
where P : class, IProperty
where I : class, IItem
{
/// <summary>
/// A helper struct wrapping a <see cref="SpanBasedStringBuilder"/> and providing file path conversion
/// as used in e.g. property expansion.
/// </summary>
/// <remarks>
/// If exactly one value is added and no concatenation takes places, this value is returned without
/// conversion. In other cases values are stringified and attempted to be interpreted as file paths
/// before concatenation.
/// </remarks>
private struct SpanBasedConcatenator : IDisposable
{
/// <summary>
/// The backing <see cref="SpanBasedStringBuilder"/>, null until the second value is added.
/// </summary>
private SpanBasedStringBuilder _builder;

/// <summary>
/// The first value added to the concatenator. Tracked in its own field so it can be returned
/// without conversion if no concatenation takes place.
/// </summary>
private object _firstObject;

/// <summary>
/// The first value added to the concatenator if it is a span. Tracked in its own field so the
/// <see cref="SpanBasedStringBuilder"/> functionality doesn't have to be invoked if no concatenation
/// takes place.
/// </summary>
private ReadOnlyMemory<char> _firstSpan;

/// <summary>
/// Adds an object to be concatenated.
/// </summary>
public void Add(object obj)
{
FlushFirstValueIfNeeded();
if (_builder != null)
{
_builder.Append(FileUtilities.MaybeAdjustFilePath(obj.ToString()));
}
else
{
_firstObject = obj;
}
}

/// <summary>
/// Adds a span to be concatenated.
/// </summary>
public void Add(ReadOnlyMemory<char> span)
{
FlushFirstValueIfNeeded();
if (_builder != null)
{
_builder.Append(FileUtilities.MaybeAdjustFilePath(span));
}
else
{
_firstSpan = span;
}
}

/// <summary>
/// Lazily initializes <see cref="_builder"/> and populates it with the first value
/// when the second value is being added.
/// </summary>
private void FlushFirstValueIfNeeded()
{
if (_firstObject != null)
{
_builder = Strings.GetSpanBasedStringBuilder();
_builder.Append(FileUtilities.MaybeAdjustFilePath(_firstObject.ToString()));
_firstObject = null;
}
else if (!_firstSpan.IsEmpty)
{
_builder = Strings.GetSpanBasedStringBuilder();
#if FEATURE_SPAN
_builder.Append(FileUtilities.MaybeAdjustFilePath(_firstSpan));
#else
_builder.Append(FileUtilities.MaybeAdjustFilePath(_firstSpan.ToString()));
#endif
_firstSpan = new ReadOnlyMemory<char>();
}
}

/// <summary>
/// Returns the result of the concatenation.
/// </summary>
/// <returns>
/// If only one value has been added and it is not a string, it is returned unchanged.
/// In all other cases (no value, one string value, multiple values) the result is a
/// concatenation of the string representation of the values, each additionally subjected
/// to file path adjustment.
/// </returns>
public object GetResult()
{
if (_firstObject != null)
{
if (_firstObject is string stringValue)
{
return FileUtilities.MaybeAdjustFilePath(stringValue);
}
return _firstObject;
}
if (!_firstSpan.IsEmpty)
{
return FileUtilities.MaybeAdjustFilePath(_firstSpan).ToString();
}
return _builder?.ToString() ?? string.Empty;
}

/// <summary>
/// Disposes of the struct by delegating the call to the underlying <see cref="SpanBasedStringBuilder"/>.
/// </summary>
public void Dispose() => _builder?.Dispose();
}

/// <summary>
/// A limit for truncating string expansions within an evaluated Condition. Properties, item metadata, or item groups will be truncated to N characters such as 'N...'.
/// Enabled by ExpanderOptions.Truncate.
Expand Down

0 comments on commit 1dd1c62

Please sign in to comment.