diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 41fce67fa7..24163f1df9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -775,36 +775,6 @@ namespace MediaBrowser.Controller.Entities return Id.ToString("N", CultureInfo.InvariantCulture); } - private List> GetSortChunks(string s1) - { - var list = new List>(); - - int thisMarker = 0; - - while (thisMarker < s1.Length) - { - char thisCh = s1[thisMarker]; - - var thisChunk = new StringBuilder(); - bool isNumeric = char.IsDigit(thisCh); - - while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - list.Add(new Tuple(thisChunk, isNumeric)); - } - - return list; - } - public virtual bool CanDelete() { if (SourceType == SourceType.Channel) @@ -951,28 +921,40 @@ namespace MediaBrowser.Controller.Entities return ModifySortChunks(sortable); } - private string ModifySortChunks(string name) + internal static string ModifySortChunks(string name) { - var chunks = GetSortChunks(name); - - var builder = new StringBuilder(); - - foreach (var chunk in chunks) + void AppendChunk(StringBuilder builder, bool isDigitChunk, ReadOnlySpan chunk) { - var chunkBuilder = chunk.Item1; - - // This chunk is numeric - if (chunk.Item2) + if (isDigitChunk && chunk.Length < 10) { - while (chunkBuilder.Length < 10) - { - chunkBuilder.Insert(0, '0'); - } + builder.Append('0', 10 - chunk.Length); } - builder.Append(chunkBuilder); + builder.Append(chunk); } + if (name.Length == 0) + { + return string.Empty; + } + + var builder = new StringBuilder(name.Length); + + int chunkStart = 0; + bool isDigitChunk = char.IsDigit(name[0]); + for (int i = 0; i < name.Length; i++) + { + var isDigit = char.IsDigit(name[i]); + if (isDigit != isDigitChunk) + { + AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart, i - chunkStart)); + chunkStart = i; + isDigitChunk = isDigit; + } + } + + AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart)); + // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); return builder.ToString().RemoveDiacritics(); } diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs index 4cd5c76c1e..534dec8d2a 100644 --- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs @@ -15,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Controller.Tests")] [assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs new file mode 100644 index 0000000000..985bbcde1f --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Controller.Entities; +using Xunit; + +namespace Jellyfin.Controller.Tests.Entities +{ + public class BaseItemTests + { + [Theory] + [InlineData("", "")] + [InlineData("1", "0000000001")] + [InlineData("t", "t")] + [InlineData("test", "test")] + [InlineData("test1", "test0000000001")] + [InlineData("1test 2", "0000000001test 0000000002")] + public void BaseItem_ModifySortChunks_Valid(string input, string expected) + => Assert.Equal(expected, BaseItem.ModifySortChunks(input)); + } +}