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 result = new List<Segment>();
var previous = (Segment?)null; var segmentBuilder = (SegmentBuilder?)null;
foreach (var segment in segments) foreach (var segment in segments)
{ {
if (previous == null) if (segmentBuilder == null)
{ {
previous = segment; segmentBuilder = new SegmentBuilder(segment);
continue; continue;
} }
// Both control codes? // Both control codes?
if (segment.IsControlCode && previous.IsControlCode) if (segment.IsControlCode && segmentBuilder.IsControlCode())
{ {
previous = Control(previous.Text + segment.Text); segmentBuilder.Append(segment.Text);
continue; continue;
} }
// Same style? // 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; continue;
} }
result.Add(previous); result.Add(segmentBuilder.Build());
previous = segment; segmentBuilder.Reset(segment);
} }
if (previous != null) if (segmentBuilder != null)
{ {
result.Add(previous); result.Add(segmentBuilder.Build());
} }
return result; return result;
@ -579,5 +579,39 @@ namespace Spectre.Console.Rendering
return list; 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;
}
}
} }
} }