Adds a segment builder for merging multiple segments

When merging a large amount of segments together we were doing a tremendous amount of allocation especially related to strings due to concatination.

This adds an internal SegmentBuilder that uses a stringbuilder for building up the text rather than creating a new instance and doing a concat operation for each segment.
This commit is contained in:
Phil Scott 2021-09-26 22:30:47 -04:00 committed by Patrik Svensson
parent 64f444114a
commit ad23855b0a

View File

@ -449,36 +449,36 @@ namespace Spectre.Console.Rendering
var result = new List<Segment>();
var previous = (Segment?)null;
var segmentBuilder = (SegmentBuilder?)null;
foreach (var segment in segments)
{
if (previous == null)
if (segmentBuilder == null)
{
previous = segment;
segmentBuilder = new SegmentBuilder(segment);
continue;
}
// Both control codes?
if (segment.IsControlCode && previous.IsControlCode)
if (segment.IsControlCode && segmentBuilder.IsControlCode())
{
previous = Control(previous.Text + segment.Text);
segmentBuilder.Append(segment.Text);
continue;
}
// Same style?
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak && !previous.IsControlCode)
if (segmentBuilder.StyleEquals(segment.Style) && !segmentBuilder.IsLineBreak() && !segmentBuilder.IsControlCode())
{
previous = new Segment(previous.Text + segment.Text, previous.Style);
segmentBuilder.Append(segment.Text);
continue;
}
result.Add(previous);
previous = segment;
result.Add(segmentBuilder.Build());
segmentBuilder.Reset(segment);
}
if (previous != null)
if (segmentBuilder != null)
{
result.Add(previous);
result.Add(segmentBuilder.Build());
}
return result;
@ -579,5 +579,39 @@ namespace Spectre.Console.Rendering
return list;
}
private class SegmentBuilder
{
private readonly StringBuilder _textBuilder = new();
private Segment _originalSegment;
public SegmentBuilder(Segment originalSegment)
{
_originalSegment = originalSegment;
Reset(originalSegment);
}
public bool IsControlCode() => _originalSegment.IsControlCode;
public bool IsLineBreak() => _originalSegment.IsLineBreak;
public bool StyleEquals(Style segmentStyle) => segmentStyle.Equals(_originalSegment.Style);
public void Append(string text)
{
_textBuilder.Append(text);
}
public Segment Build()
{
return new Segment(_textBuilder.ToString(), _originalSegment.Style, _originalSegment.IsLineBreak,
_originalSegment.IsControlCode);
}
public void Reset(Segment segment)
{
_textBuilder.Clear();
_textBuilder.Append(segment.Text);
_originalSegment = segment;
}
}
}
}