Reduce allocations

This commit is contained in:
Bond_009 2021-09-19 20:53:31 +02:00
parent a3a8e058bc
commit a6d1e542e6
14 changed files with 105 additions and 48 deletions

View file

@ -11,6 +11,7 @@ using System.Net.Http;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -82,9 +83,7 @@ namespace Emby.Dlna.Eventing
if (!string.IsNullOrEmpty(header)) if (!string.IsNullOrEmpty(header))
{ {
// Starts with SECOND- // Starts with SECOND-
header = header.Split('-')[^1]; if (int.TryParse(header.AsSpan().RightPart('-'), NumberStyles.Integer, _usCulture, out var val))
if (int.TryParse(header, NumberStyles.Integer, _usCulture, out var val))
{ {
return val; return val;
} }

View file

@ -10,6 +10,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using Jellyfin.XmlTv; using Jellyfin.XmlTv;
using Jellyfin.XmlTv.Entities; using Jellyfin.XmlTv.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
@ -89,11 +90,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return UnzipIfNeeded(path, cacheFile); return UnzipIfNeeded(path, cacheFile);
} }
private string UnzipIfNeeded(string originalUrl, string file) private string UnzipIfNeeded(ReadOnlySpan<char> originalUrl, string file)
{ {
string ext = Path.GetExtension(originalUrl.Split('?')[0]); ReadOnlySpan<char> ext = Path.GetExtension(originalUrl.LeftPart('?'));
if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase)) if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase))
{ {
try try
{ {

View file

@ -36,7 +36,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
private readonly IStreamHelper _streamHelper; private readonly IStreamHelper _streamHelper;
private readonly JsonSerializerOptions _jsonOptions; private readonly JsonSerializerOptions _jsonOptions;
@ -50,7 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IServerApplicationHost appHost, IServerApplicationHost appHost,
ISocketFactory socketFactory, ISocketFactory socketFactory,
INetworkManager networkManager,
IStreamHelper streamHelper, IStreamHelper streamHelper,
IMemoryCache memoryCache) IMemoryCache memoryCache)
: base(config, logger, fileSystem, memoryCache) : base(config, logger, fileSystem, memoryCache)
@ -58,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_appHost = appHost; _appHost = appHost;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_networkManager = networkManager;
_streamHelper = streamHelper; _streamHelper = streamHelper;
_jsonOptions = JsonDefaults.Options; _jsonOptions = JsonDefaults.Options;
@ -70,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
protected override string ChannelIdPrefix => "hdhr_"; protected override string ChannelIdPrefix => "hdhr_";
private string GetChannelId(TunerHostInfo info, Channels i) private string GetChannelId(Channels i)
=> ChannelIdPrefix + i.GuideNumber; => ChannelIdPrefix + i.GuideNumber;
internal async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) internal async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
@ -103,7 +100,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
Name = i.GuideName, Name = i.GuideName,
Number = i.GuideNumber, Number = i.GuideNumber,
Id = GetChannelId(tuner, i), Id = GetChannelId(i),
IsFavorite = i.Favorite, IsFavorite = i.Favorite,
TunerHostId = tuner.Id, TunerHostId = tuner.Id,
IsHD = i.HD, IsHD = i.HD,
@ -255,7 +252,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
var tuners = new List<LiveTvTunerInfo>(); var tuners = new List<LiveTvTunerInfo>(model.TunerCount);
var uri = new Uri(GetApiUrl(info)); var uri = new Uri(GetApiUrl(info));
@ -264,10 +261,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Legacy HdHomeruns are IPv4 only // Legacy HdHomeruns are IPv4 only
var ipInfo = IPAddress.Parse(uri.Host); var ipInfo = IPAddress.Parse(uri.Host);
for (int i = 0; i < model.TunerCount; ++i) for (int i = 0; i < model.TunerCount; i++)
{ {
var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1); var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1);
var currentChannel = "none"; // @todo Get current channel and map back to Station Id var currentChannel = "none"; // TODO: Get current channel and map back to Station Id
var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false); var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv; var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
tuners.Add(new LiveTvTunerInfo tuners.Add(new LiveTvTunerInfo
@ -455,28 +452,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Path = url, Path = url,
Protocol = MediaProtocol.Udp, Protocol = MediaProtocol.Udp,
MediaStreams = new List<MediaStream> MediaStreams = new List<MediaStream>
{ {
new MediaStream new MediaStream
{ {
Type = MediaStreamType.Video, Type = MediaStreamType.Video,
// Set the index to -1 because we don't know the exact index of the video stream within the container // Set the index to -1 because we don't know the exact index of the video stream within the container
Index = -1, Index = -1,
IsInterlaced = isInterlaced, IsInterlaced = isInterlaced,
Codec = videoCodec, Codec = videoCodec,
Width = width, Width = width,
Height = height, Height = height,
BitRate = videoBitrate, BitRate = videoBitrate,
NalLengthSize = nal NalLengthSize = nal
}, },
new MediaStream new MediaStream
{ {
Type = MediaStreamType.Audio, Type = MediaStreamType.Audio,
// Set the index to -1 because we don't know the exact index of the audio stream within the container // Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1, Index = -1,
Codec = audioCodec, Codec = audioCodec,
BitRate = audioBitrate BitRate = audioBitrate
} }
}, },
RequiresOpening = true, RequiresOpening = true,
RequiresClosing = true, RequiresClosing = true,
BufferMs = 0, BufferMs = 0,
@ -551,7 +548,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
} }
} }
var profile = streamId.Split('_')[0]; var profile = streamId.AsSpan().LeftPart('_').ToString();
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channel.Id, streamId, profile); Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channel.Id, streamId, profile);

View file

@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
try try
{ {
numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/')[^1]); numberString = Path.GetFileNameWithoutExtension(mediaUrl.AsSpan().RightPart('/')).ToString();
if (!IsValidChannelNumber(numberString)) if (!IsValidChannelNumber(numberString))
{ {

View file

@ -7,6 +7,7 @@ using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -199,7 +200,7 @@ namespace Jellyfin.Api.Controllers
throw new ResourceNotFoundException(nameof(response.Content.Headers.ContentType)); throw new ResourceNotFoundException(nameof(response.Content.Headers.ContentType));
} }
var ext = response.Content.Headers.ContentType.MediaType.Split('/')[^1]; var ext = response.Content.Headers.ContentType.MediaType.AsSpan().RightPart('/').ToString();
var fullCachePath = GetFullCachePath(urlHash + "." + ext); var fullCachePath = GetFullCachePath(urlHash + "." + ext);
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid."); var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Api.Models.StreamingDtos;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -81,7 +82,7 @@ namespace Jellyfin.Api.Helpers
throw new ResourceNotFoundException(nameof(httpRequest.Path)); throw new ResourceNotFoundException(nameof(httpRequest.Path));
} }
var url = httpRequest.Path.Value.Split('.')[^1]; var url = httpRequest.Path.Value.AsSpan().RightPart('.').ToString();
if (string.IsNullOrEmpty(streamingRequest.AudioCodec)) if (string.IsNullOrEmpty(streamingRequest.AudioCodec))
{ {

View file

@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var size = part.Split('=', 2)[^1]; var size = part.Split('=', 2)[^1];
int? scale = null; int? scale = null;
if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1) if (size.Contains("kb", StringComparison.OrdinalIgnoreCase))
{ {
scale = 1024; scale = 1024;
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase); size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
@ -139,7 +139,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var rate = part.Split('=', 2)[^1]; var rate = part.Split('=', 2)[^1];
int? scale = null; int? scale = null;
if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1) if (rate.Contains("kbits/s", StringComparison.OrdinalIgnoreCase))
{ {
scale = 1024; scale = 1024;
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase); rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -331,7 +332,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
if (!string.IsNullOrWhiteSpace(text)) if (!string.IsNullOrWhiteSpace(text))
{ {
if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out var runtime)) if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, _usCulture, out var runtime))
{ {
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks; item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
} }

View file

@ -1378,7 +1378,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{ {
var disc = tags.GetValueOrDefault(tagName); var disc = tags.GetValueOrDefault(tagName);
if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.Split('/')[0], out var discNum)) if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.AsSpan().LeftPart('/'), out var discNum))
{ {
return discNum; return discNum;
} }

View file

@ -18,14 +18,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{ {
writer.WriteLine("WEBVTT"); writer.WriteLine("WEBVTT");
writer.WriteLine(string.Empty); writer.WriteLine();
writer.WriteLine("REGION"); writer.WriteLine("REGION");
writer.WriteLine("id:subtitle"); writer.WriteLine("id:subtitle");
writer.WriteLine("width:80%"); writer.WriteLine("width:80%");
writer.WriteLine("lines:3"); writer.WriteLine("lines:3");
writer.WriteLine("regionanchor:50%,100%"); writer.WriteLine("regionanchor:50%,100%");
writer.WriteLine("viewportanchor:50%,90%"); writer.WriteLine("viewportanchor:50%,90%");
writer.WriteLine(string.Empty); writer.WriteLine();
foreach (var trackEvent in info.TrackEvents) foreach (var trackEvent in info.TrackEvents)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();

View file

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Extensions;
namespace MediaBrowser.Model.Net namespace MediaBrowser.Model.Net
{ {
@ -221,7 +222,7 @@ namespace MediaBrowser.Model.Net
} }
// handle text/html; charset=UTF-8 // handle text/html; charset=UTF-8
mimeType = mimeType.Split(';')[0]; mimeType = mimeType.AsSpan().LeftPart(';').ToString();
if (_extensionLookup.TryGetValue(mimeType, out string? result)) if (_extensionLookup.TryGetValue(mimeType, out string? result))
{ {

View file

@ -8,6 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Providers; using MediaBrowser.Common.Providers;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -474,7 +475,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(text)) if (!string.IsNullOrWhiteSpace(text))
{ {
if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, UsCulture, out var runtime)) if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, UsCulture, out var runtime))
{ {
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks; item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
} }

View file

@ -27,5 +27,39 @@ namespace Jellyfin.Extensions
return count; return count;
} }
/// <summary>
/// Returns the part on the left of the <c>needle</c>.
/// </summary>
/// <param name="haystack">The string to seek.</param>
/// <param name="needle">The needle to find.</param>
/// <returns>The part left of the <paramref name="needle" />.</returns>
public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, char needle)
{
var pos = haystack.IndexOf(needle);
return pos == -1 ? haystack : haystack[..pos];
}
/// <summary>
/// Returns the part on the right of the <c>needle</c>.
/// </summary>
/// <param name="haystack">The string to seek.</param>
/// <param name="needle">The needle to find.</param>
/// <returns>The part right of the <paramref name="needle" />.</returns>
public static ReadOnlySpan<char> RightPart(this ReadOnlySpan<char> haystack, char needle)
{
var pos = haystack.LastIndexOf(needle);
if (pos == -1)
{
return haystack;
}
if (pos == haystack.Length - 1)
{
return ReadOnlySpan<char>.Empty;
}
return haystack[(pos + 1)..];
}
} }
} }

View file

@ -14,5 +14,26 @@ namespace Jellyfin.Extensions.Tests
{ {
Assert.Equal(count, str.AsSpan().Count(needle)); Assert.Equal(count, str.AsSpan().Count(needle));
} }
[Theory]
[InlineData("", 'q', "")]
[InlineData("Banana split", ' ', "Banana")]
[InlineData("Banana split", 'q', "Banana split")]
public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult)
{
var result = str.AsSpan().LeftPart(needle).ToString();
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("", 'q', "")]
[InlineData("Banana split", ' ', "split")]
[InlineData("Banana split", 'q', "Banana split")]
[InlineData("Banana split.", '.', "")]
public void RightPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult)
{
var result = str.AsSpan().RightPart(needle).ToString();
Assert.Equal(expectedResult, result);
}
} }
} }