Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
95 changes: 95 additions & 0 deletions src/libse/Common/StringBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System.Text;

namespace Nikse.SubtitleEdit.Core.Common
{
public static class StringBuilderExtensions
{
/// <summary>
/// Trims leading/trailing whitespace inside the builder - the "sb.ToString().Trim()"
/// idiom allocates the whole output an extra time.
/// </summary>
public static void Trim(this StringBuilder sb)
{
while (sb.Length > 0 && char.IsWhiteSpace(sb[sb.Length - 1]))
{
sb.Length--;
}

var start = 0;
while (start < sb.Length && char.IsWhiteSpace(sb[start]))
{
start++;
}

if (start > 0)
{
sb.Remove(0, start);
}
}

public static bool StartsWith(this StringBuilder sb, char c)
{
return sb.Length > 0 && sb[0] == c;
}

public static bool EndsWith(this StringBuilder sb, char c)
{
return sb.Length > 0 && sb[sb.Length - 1] == c;
}

// Same count as scanning sb.ToString() without materializing the whole
// accumulated text into a fresh string.
public static int CountChar(this StringBuilder sb, char c)
{
var count = 0;
#if NET8_0_OR_GREATER
foreach (var chunk in sb.GetChunks())
{
foreach (var ch in chunk.Span)
{
if (ch == c)
{
count++;
}
}
}
#else
// GetChunks is missing from the netstandard2.1 reference assemblies.
for (var i = 0; i < sb.Length; i++)
{
if (sb[i] == c)
{
count++;
}
}
#endif
return count;
}

// Matches "{0:00}"/"{0:000}"-style formatting: sign first, then the absolute value
// padded with leading zeros to minDigits. Negates on a long so int.MinValue does
// not overflow.
public static void AppendNumber(this StringBuilder sb, int value, int minDigits)
{
var v = (long)value;
if (v < 0)
{
sb.Append('-');
v = -v;
}

var digitCount = 1;
for (var rest = v; rest >= 10; rest /= 10)
{
digitCount++;
}

for (; digitCount < minDigits; digitCount++)
{
sb.Append('0');
}

sb.Append(v);
}
}
}
10 changes: 0 additions & 10 deletions src/libse/Common/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,11 @@ public static bool StartsWith(this string s, char c)
return s.Length > 0 && s[0] == c;
}

public static bool StartsWith(this StringBuilder sb, char c)
{
return sb.Length > 0 && sb[0] == c;
}

public static bool EndsWith(this string s, char c)
{
return s.Length > 0 && s[s.Length - 1] == c;
}

public static bool EndsWith(this StringBuilder sb, char c)
{
return sb.Length > 0 && sb[sb.Length - 1] == c;
}

public static bool Contains(this string source, char value)
{
return source.IndexOf(value) >= 0;
Expand Down
28 changes: 5 additions & 23 deletions src/libse/SubtitleFormats/AdvancedSubStationAlpha.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ string HeaderTemplate() => headerTemplate ??= $@"[Script Info]

// Trim inside the builder instead of "sb.ToString().Trim() + newline", which
// allocated the whole multi-megabyte output twice more.
TrimBuilder(sb);
sb.Trim();
return sb.Append(Environment.NewLine).ToString();
}

Expand All @@ -340,13 +340,13 @@ private static StringBuilder AppendTimeCode(StringBuilder sb, TimeCode timeCode)
fragment = 0;
}

AppendNumber(sb, ts.Days * 24 + ts.Hours, 1);
sb.AppendNumber(ts.Days * 24 + ts.Hours, 1);
sb.Append(':');
AppendNumber(sb, ts.Minutes, 2);
sb.AppendNumber(ts.Minutes, 2);
sb.Append(':');
AppendNumber(sb, ts.Seconds, 2);
sb.AppendNumber(ts.Seconds, 2);
sb.Append('.');
AppendNumber(sb, fragment, 2);
sb.AppendNumber(fragment, 2);
return sb;
}

Expand All @@ -368,24 +368,6 @@ private static void AppendTextWithAssaNewLines(StringBuilder sb, string text)
sb.Append(span);
}

// Matches "{0:00}"-style formatting: sign first, then the absolute value padded with
// leading zeros to minDigits.
private static void AppendNumber(StringBuilder sb, int value, int minDigits)
{
if (value < 0)
{
sb.Append('-');
value = -value;
}

if (minDigits >= 2 && value < 10)
{
sb.Append('0');
}

sb.Append(value);
}

public static string GetHeaderAndStylesFromSubStationAlpha(string header)
{
var scriptInfo = string.Empty;
Expand Down
2 changes: 1 addition & 1 deletion src/libse/SubtitleFormats/SubRip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public override string ToText(Subtitle subtitle, string title)
sb.Append(p.Text).Append(Environment.NewLine).Append(Environment.NewLine);
}

TrimBuilder(sb);
sb.Trim();
return sb.Append(Environment.NewLine).Append(Environment.NewLine).ToString();
}

Expand Down
2 changes: 1 addition & 1 deletion src/libse/SubtitleFormats/SubStationAlpha.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ [V4 Styles]

// Trim inside the builder instead of "sb.ToString().Trim() + newline", which
// allocated the whole output twice more (same fix as [V4+ Styles]).
TrimBuilder(sb);
sb.Trim();
return sb.Append(Environment.NewLine).ToString();
}

Expand Down
23 changes: 0 additions & 23 deletions src/libse/SubtitleFormats/SubtitleFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -563,29 +563,6 @@ public static int FramesToMillisecondsMax999(double frames)
public bool BatchMode { get; set; }
public double? BatchSourceFrameRate { get; set; }

/// <summary>
/// Trims leading/trailing whitespace inside the builder - the "sb.ToString().Trim()"
/// idiom in ToText implementations allocates the whole output an extra time.
/// </summary>
protected static void TrimBuilder(StringBuilder sb)
{
while (sb.Length > 0 && char.IsWhiteSpace(sb[sb.Length - 1]))
{
sb.Length--;
}

var start = 0;
while (start < sb.Length && char.IsWhiteSpace(sb[start]))
{
start++;
}

if (start > 0)
{
sb.Remove(0, start);
}
}

public static string ToUtf8XmlString(XmlDocument xml, bool omitXmlDeclaration = false)
{
var settings = new XmlWriterSettings
Expand Down
25 changes: 3 additions & 22 deletions src/libuilogic/Translate/MergeAndSplitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,25 +488,6 @@ private static void InitializeFirstItem(MergeResult result, MergeContext context
}
}

// Same count as Utilities.CountTagInText(builder.ToString(), c) without materializing the
// whole accumulated text into a fresh string per merged row.
private static int CountChar(StringBuilder builder, char c)
{
var count = 0;
foreach (var chunk in builder.GetChunks())
{
foreach (var ch in chunk.Span)
{
if (ch == c)
{
count++;
}
}
}

return count;
}

private static readonly int NewLineUrlEncodeLength = Utilities.UrlEncodeLength(Environment.NewLine);

private static bool ExceedsMaxSize(MergeResult result, MergeContext context, TranslateRow currentRow, int maxTextSize)
Expand Down Expand Up @@ -590,7 +571,7 @@ private static void ProcessEmptyRow(MergeResult result, MergeContext context, in
context.CurrentItem.TextIndexStart = result.Text.Length;
context.CurrentItem.TextIndexEnd = result.Text.Length;
context.CurrentItem.EndChar = endChar;
context.CurrentItem.EndCharOccurrences = CountChar(context.TextBuilder, endChar);
context.CurrentItem.EndCharOccurrences = context.TextBuilder.CountChar(endChar);
result.MergeResultItems.Add(context.CurrentItem);
}

Expand Down Expand Up @@ -621,7 +602,7 @@ private static void ProcessSentenceEndingRow(MergeResult result, MergeContext co
{
var endChar = result.Text[^1];
context.CurrentItem.EndChar = endChar;
context.CurrentItem.EndCharOccurrences = CountChar(context.TextBuilder, endChar);
context.CurrentItem.EndCharOccurrences = context.TextBuilder.CountChar(endChar);
context.CurrentItem.TextIndexEnd = result.Text.Length;
}

Expand Down Expand Up @@ -681,7 +662,7 @@ private static void FinalizeResult(MergeResult result, MergeContext context)
{
var endChar = result.Text[^1];
context.CurrentItem.EndChar = endChar;
context.CurrentItem.EndCharOccurrences = CountChar(context.TextBuilder, endChar);
context.CurrentItem.EndCharOccurrences = context.TextBuilder.CountChar(endChar);
}
}

Expand Down
Loading
Loading